ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #818 (Div. 2) CF1717 解题报告

2022-09-04 14:31:23  阅读:206  来源: 互联网

标签:ch gcd int Codeforces times 818 le CF1717 getchar


Codeforces Round #818 (Div. 2) CF1717 解题报告

A

Description

求出满足\(1\le a,b\le N,\frac{\operatorname{lcm}(a,b)}{\gcd(a,b)}\le 3\)的二元组\((a,b)\)的数目。

\(N\le 10^8\)

Sol

由\(a\times b=\operatorname{lcm}(a,b)\times \gcd(a,b)\)转化上述分式,可得

\[a\times b\le 3\times \operatorname{gcd}^2(a,b) \]

设\(a=mp,b=np(m,n\in N^*,\gcd(m,n)=1)\),则上式可转化为

\[mnp^2\le 3p^2\Longrightarrow mn\le 3 \]

于是\((m,n)\)只有\((1,1),(1,2),(2,1),(3,1),(1,3)\)这\(5\)种情况。

最终答案即为\(N+\lfloor\frac N2\rfloor\times 2+\lfloor\frac N3\rfloor\times 2\)

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int Read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch) && ch != '-')  ch = getchar();
	if(ch == '-')  f = -1, ch = getchar();
	while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return (f == -1) ? -x : x;
}

signed main() {
	int T = Read();
	while(T--) {
		int x = Read();
		printf("%lld\n", x + (x / 2) * 2 + (x / 3) * 2);
	}
    return 0;
}

B

Description

构造一个\(n\times n\)的包含\(.,X\)的矩阵,要求每个\(1\times k,k\times 1\)的子矩阵内至少有一个\(X\)且位置\((a,b)\)为\(X\)且\(X\)的数量最小,保证\(n\)为\(k\)的倍数。

\(1\le k,a,b\le n\le 500\)

Sol

首先可以发现,由于\(n\)为\(k\)的倍数,所以构造出来的\(X\)最少的矩阵一定是每个\(1\times k, k\times 1\)的子矩阵内恰好有一个\(1\),第一行形如\(X..X..X..\),后面的行由第一行平移得出。

找到一个合法矩阵后直接把矩阵从某一个有\(X\)的位置平移到\((a,b)\)即可。

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int Read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch) && ch != '-')  ch = getchar();
	if(ch == '-')  f = -1, ch = getchar();
	while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return (f == -1) ? -x : x;
}
int mp[505][505];
signed main() {
	int T = Read();
	while(T--) {
		memset(mp, 0, sizeof(mp));
		int n = Read(), k = Read(), r = Read(), c = Read();
		for(int i = 1; i <= n; i++)
			for(int j = (i - 1) % k + 1; j <= n; j += k)  mp[i][j] = 1;
		int flag = 0;
		for(int i = 1; i <= n; i++)  if(mp[i][c]) {flag = i; break ;}
		int num = r - flag;
		for(int i = 1; i <= n; i++) {
			int nw = (i - num + n - 1) % n + 1;
			for(int j = 1; j <= n; j++)  putchar(mp[nw][j] ? 'X' : '.');
			puts("");
		}
	}
    return 0;
}

C

Description

给定两个长为\(n\)的数列\(\{a\},\{b\}\),定义一次操作为选择一个数\(i\),若\(i<n\)且\(a_i\le a_{i+1}\)或\(i=n\)且\(a_i\le a_1\),则令\(a_i=a_i+1\).

问能否在若干次操作后把\(\{a\}\)变为\(\{b\}\)

\(n\le 2\times 10^5\)

Sol

由于\(a_i\)只增不减,所以当\(a_i>b_i\)时直接无解。

又由于\(a_i\le a_{i+1}\)时才能增加,即增加后\(a_i\le a_{i+1}+1\),故当\(b_{i}>b_{i+1}+1\)且\(a_i\not =b_i\)时也无解。

其余情况均有解,可以构造如下:

取\(tmp=\min{\{b_i\}}\),将所有\(a_i\)变为\(\min\{a_i,b_i,tmp\}\),易证这是可以做到的。

然后将\(tmp+1\),将所有\(a_i\)变为\(\min\{a_i,b_i,tmp\}\)

重复上一步操作直到\(tmp=\max\{b_i\}\)。

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int Read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch) && ch != '-')  ch = getchar();
	if(ch == '-')  f = -1, ch = getchar();
	while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return (f == -1) ? -x : x;
}
int n, a[200005], b[200005];
signed main() {
	int T = Read();
	while(T--) {
		n = Read();
		for(int i = 1; i <= n; i++)  a[i] = Read();
		for(int i = 1; i <= n; i++)  b[i] = Read();
		int flag = 1;
		for(int i = 1; i <= n; i++) {
			if(a[i] > b[i])  flag = 0;
			int nxt = ((i == n) ? 1 : i + 1);
			if(b[i] > b[nxt] + 1 && a[i] != b[i])  flag = 0;
		}
		puts(flag ? "YES" : "NO");
	}
    return 0;
}

D

Description

有\(2^n\)个人打淘汰赛,共\(n\)轮,\(A\)可以决定每一轮中每一对比赛者谁获胜,\(B\)可以在\(A\)决定完毕后更改至多\(k\)对比赛者的获胜情况,问\(A,B\)都进行最优操作下最终获胜者的最大编号。

\(n\le 10^5,k\le \min\{2^n-1,10^9\}\)

Sol

首先可以观察到,如果\(B\)想让一个人赢,那么\(B\)只需要调整有关他的比赛且他在该比赛中失败的场次。

换言之,如果要让\(C\)赢,那么就要从比赛的完全二叉树底代表\(C\)的点开始,一层一层的往上跳,如果该层为失败,则调整。

将一个点到树根路线上的输赢看做一个\(01\)串(称为输赢串),输为\(0\),赢为\(1\),那么调整次数即为\(0\)的个数。

\(A\)要保证\(B\)调整后所能得到的最大编号最小,那么\(A\)只能把编号大的数置于输赢串中\(0\)尽量多的位置,同样的,\(B\)也只能选择输赢串中的\(0\)个数\(\le k(\)等价于\(=k\),因为\(A\)肯定不会把大的放前面\()\)中编号最大的人。

考虑\(0\)个数为\(x\)的串有多少个,答案即为\(C_{n}^x\),组合的意义很明显。

于是\(B\)挑的人即为\(C_n^0+C_n^1+...+C_n^k\),当\(k\ge n\)时为\(2^n\)。

Code

点击查看代码
#include<bits/stdc++.h>
#define Mod 1000000007
#define int long long
using namespace std;
int Read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch) && ch != '-')  ch = getchar();
	if(ch == '-')  f = -1, ch = getchar();
	while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return (f == -1) ? -x : x;
}
int qpow(int x, int k) {
	int res = 1;
	while(k) {
		if(k & 1)  res = res * x % Mod;
		x = x * x % Mod;
		k >>= 1;
	}
	return res;
}
int fac[100005], finv[100005], inv[100005];
int C(int n, int m) {
	return fac[n] * finv[m] % Mod * finv[n - m] % Mod;
}
signed main() {
	fac[0] = fac[1] = inv[0] = inv[1] = finv[0] = finv[1] = 1;
	for(int i = 2; i <= 100000; i++) {
		fac[i] = fac[i - 1] * i % Mod;
		inv[i] = (Mod - Mod / i) * inv[Mod % i] % Mod;
		finv[i] = finv[i - 1] * inv[i] % Mod;
	}
	int n = Read(), k = Read();
	if(k >= n) {
		printf("%lld\n", qpow(2, n));
		return 0;
	}
	int sum = 0;
	for(int i = 0; i <= k; i++)  sum = (sum + C(n, i)) % Mod;
	cout << sum << endl;
    return 0;
}

E

Description

对于所有满足\(a+b+c=n\)的三元组\((a,b,c)\)求出\(\sum\operatorname{lcm}(c,\gcd(a,b))\)。

\(3\le n\le 10^5\)

Sol

稍微用\(a\times b=\operatorname{lcm}(a,b)\times \gcd(a,b)\)转化一下,得

\[\sum\operatorname{lcm}(c,\gcd(a,b))=\sum\frac{c\times \gcd(a,b)}{\gcd(a,b,c)}=\sum_{c=1}^{n-2}c(\sum\frac{\gcd(a,b)}{\gcd(a,b,c)}) \]

由于\(a+b=n-c\),所以\(\gcd(a,b)\)只可能是\(n-c\)的约数,为\(O(2\sqrt{n-c})\)级别(实际上远远达不到),所以枚举\(c\)和\(\gcd(a,b)\)即可做到快速计算。

然后当\(\gcd(a,b)=p\)时不能算入诸如\(a=2p,b=4p\)的\((a,b)\),容斥一下减掉即可。

Code

点击查看代码
#include<bits/stdc++.h>
#define Mod 1000000007
#define int long long
using namespace std;
int Read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch) && ch != '-')  ch = getchar();
	if(ch == '-')  f = -1, ch = getchar();
	while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return (f == -1) ? -x : x;
}
int n, f[100005], mu[100005], prime[100005], flag[100005], cnt;
vector<int> ys[100005];
void sieve(){
	mu[1] = 1;
	for(int i = 2; i <= 100000; i++){
		if(!flag[i])  prime[++cnt] = i, mu[i] = -1;
		for(int j = 1; j <= cnt && i * prime[j] <= 100000; j++){
			flag[i * prime[j]] = 1;
			if(i % prime[j] == 0)  break;
			mu[i * prime[j]] -= mu[i];
		}
	}
}
int gcd(int x, int y) {
	return (x % y == 0) ? y : gcd(y, x % y);
}
signed main() {
	sieve();
	for(int i = 1; i <= 100000; i++)
		for(int j = i; j <= 100000; j += i)  ys[j].push_back(i);
	for(int i = 1; i <= 100000; i++) {
		for(int j = 0; j < ys[i].size(); j++)
			f[i] += mu[ys[i][j]] * ((i / ys[i][j]) - 1);
	}
	n = Read();
	int sum = 0;
	for(int i = 1; i <= n - 2; i++) {
		int res = n - i;
		for(int j = 0; j < ys[res].size(); j++) {
			int val = ys[res][j] / gcd(ys[res][j], i);
			int num = f[res / ys[res][j]];
			sum = (sum + i * val % Mod * num % Mod) % Mod;
		}
	}
	cout << sum << endl;
    return 0;
}

F

Description

给定三个长为\(n\)的数列\(\{a\},\{b\},\{s\}\)与\(m\)组操作\((u_i,v_i)\),\(\{b\}\)初始全为\(0\)。

对于每一组操作,你可以选择令\(b_{u_i}=b_{u_i}+1,b_{v_i}=b_{v_i}-1\)或\(b_{u_i}=b_{u_i}-1,b_{v_i}=b_{v_i}+1\)。

问执行完这\(m\)个操作后能否使对于所有的\(s_i=1\)满足\(a_i=b_i\),若能则输出具体操作。

Sol

将问题转化到图上,则问题变为给一张无向图的边定向,使一些指定点的入度减去出度为一个给定的值。

如果不是每个边都必须用到,那么就是一个经典的网络流问题。

但现在每条边都要用到,我们可以改变建图方式来强行让每条能用的边都用上。

先化边为点,我们从虚拟源点向每条边引出一条流量为\(1\)的边,由于我们跑的是最大流,所以如果一条边能用必然会被用上。

然后对于每条边向\(u_i,v_i\)分别连容量为\(1\)的边,流过一条边就代表对应点的权值加\(1\)

现在我们没有讨论\(-1\)的情况,而我们需要将边向点连出的边没流的变为\(-1\),设一共有\(du_i\)条边连接了\(i\),其中流了\(x\)条边进\(i\),那么需要满足\(x-(du_i-x)=a_i\),即\(x=\frac{du_i+a_i}2\),然后从每个点向虚拟汇点连接一条容量为\(x\)的边。

如果\(s_i=0\),那么没有要求,向汇点连一条流量为\(+\infin\)的边即可。

不过最大流可能不唯一,我们需要保证\(s_i=1\)的边优先被流满,所以先不连\(s_i=0\)的边跑一遍网络流,再连上跑一遍,如果所有\(s_i=1\)的点到汇点的边流满且最大流\(=m\),那么就成立,反正不成立。

输出操作的时候直接看一条边\((i,u_i)\)是否被流,是则输出\((v_i,u_i)\),反之输出\((u_i,v_i)\)

标签:ch,gcd,int,Codeforces,times,818,le,CF1717,getchar
来源: https://www.cnblogs.com/verjun/p/16655059.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有