Codeforces Round 969 (Div
\(\Huge{Codeforces Round 969 (Div. 2)}\)
比赛地址:Codeforces Round 969 (Div. 2)
A. Dora's Set
题意
给出区间\([l,r]\),每次可以执行以下操作:
- 任选三个不同的数字\(a,b,c\),使得\(gcd(a,b)=gcd(b,c)=gcd(c,a)=1\)。然后删掉\(a,b,c\)。
求最多可以执行多少次操作?
思路
跟据题意,每次操作需要选三个数字,且三个数字间两两互质。那么我们只需要知道以下两个性质即可:
- 相邻两个数字互质。
- 相邻两个奇数互质。
那么本题就显而易见了,找到区间[l,r]中所有连续的\(a_i,a_{i+1},a_{i+2}\),且\(a_i\)为奇数即可。
标程
void Solved() {
int a, b; cin >> a >> b;
if(!(a & 1)) a ++;
int res = 0, i;
for(i = a; i + 2 <= b; i += 4 ) {
res ++;
}
cout << res << endl;
}
B. Index and Maximum Value
题意
给出一个长度为\(n\)的数组,然后执行m次操作,给出两种操作:
+ l r:给出两个整数\(l,r\),对于数组中所有\(l \leq a_i \leq r\),令\(a_i=a_i+1\)- l r:给出两个整数\(l,r\),对于数组中所有\(l \leq a_i \leq r\),令\(a_i=a_i-1\)
找出m次操作后数组中的最大值。
思路
容易想到,结果要求数组最大值。上面两种操作不会对数组的大小顺序产生影响,即原最大值在m次操作后依然是最大值。
标程
void Solved() {
int n, m; cin >> n >> m;
vector<int> a(n);
int res = 0;
for(auto &i : a) cin >> i, res = max(res, i);
while(m -- ) {
char ch;
int l, r; cin >> ch >> l >> r;
if(res >= l && res <= r) {
res += (ch == '+' ? 1 : -1);
}
cout << res << ' ';
}
cout << endl;
}
C. Dora and C++
题意
给出一个长度为\(n\)的数组,然后给出两个数字a,b,然后可以选择执行以下两种操作任意次:
- 选择数组中的一个数字a_i,然后a_i+=a。
- 选择数组中的一个数字a_i,然后a_i+=b。
操作可以执行任意次,最终需要最小化数组中最大、最小值的差值。
思路
很容易想到,我们可以通过将每个数字都加上若干a和若干b,使得所有数字间的差值最小。那么我们该如何操作呢?
跟据裴蜀定理,
裴蜀定理内容:
对任何整数\(a,b\)和它们的最大公约数\(d\),关于未知数\(x,y\)的线性不定方程(称为裴蜀等式):若\(a,b\)是整数,且\(gcd(a,b)=d\),那么对于任意的整数都\(x,y,ax+by\)一定是\(d\)的倍数,特别地,一定存在整数\(x,y\),使\(ax+by=d\)成立。 ——百度百科
简化为:
因此,对于任意两个相等整数\(x=y\),我们可以分别加上若干a和若干b,使得\(x=y\pm p\times gcd(a,b)\),那么对于两个不相等整数\(x,y\),我们依然可以通过若干次操作使得其差值增大或减小\(p\times gcd(a,b)\),其中,\(p\)为任意整数。
那么利用该性质,本题结果必然会小于\(gcd(a,b)\),我们只需要将数组中的每个数对\(gcd(a,b)\)取模即可,同时需要排序,然后取\(min\)操作:\(min(f[i-1]+c-f[i],f[i]-f[i-1])\)即可。
标程
#define int long long
void Solved() {
int n, a, b; cin >> n >> a >> b;
int c = gcd(a, b);
vector<int> f(n);
for(auto &i : f) cin >> i, i %= c;
sort(ALL(f));
int res = f[n - 1] - f[0];
for(int i = 1; i < n; i ++ ) {
res = min(res, f[i - 1] + c - f[i]);
}
cout << res << endl;
}
D. Iris and game on the Tree
题意
给出一棵树,树上每个节点都有三种状态:0、1、?。对于某叶子节点,根据其构造一个字符串:从根节点到该节点路径上的所有节点的状态。在该字符串上,所有10与01子字符串个数的差值即为该叶子节点的权重。
树的分数定义为树中非零权重的叶子数。
现在Alice和Bob交替将节点状态中的“?”改为“0”或“1”,Alice先手,且希望最大化树的分数,Bob希望最小化树的分数。
当“?”被修改完时游戏结束,求最终树的得分?
思路
通过观察可以发现,一条从根到叶子的路径是否有得分,只取决于根节点与叶子节点是否相同。相同则无贡献,不同则有贡献。
那么双方博弈就只用看根节点和叶子的情况,先手抢占有利的一方即可。
只有一种特殊情况下需要考虑到路径节点上的“?”,即当根节点为“?”,且叶子节点中“0”,“1”的数量相等时(也可以都为0),此时双方处于平衡状态,在根节点和叶子节点上先手的一方反而会成为后手,所以Alice需要利用中间路径上的”?”来拉扯一下,如果中间路径上的“?”数量为奇数,那么通过拉扯就可以使先后手转换,那么Bob就不会亏掉这一分。
标程
void Solved() {
int n; cin >> n;
vector<vector<int>> g(n + 1);
for(int i = 1; i < n; i ++ ) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
string s; cin >> s;
int s0 = 0, s1 = 0, a = 0;
int b = count(s.brgin(), s.end(), '?');
for(int i = 2; i <= n; i ++ ) {
if(g[i].size() != 1) continue; //只判断叶子节点
if(s[i - 1] == '0') s0 ++;
else if(s[i - 1] == '1') s1 ++;
else a ++;
}
int res = 0;
//判断根节点
if(s[0] == '0') res = s1;
else if(s[0] == '1') res = s0;
else {
res = max(s0, s1);
if(s0 == s1 && (b - a - 1) % 2 == 1 && (b - a - 1) > 0) {
a ++;
}
a --;
}
res += (a + 1) / 2;
cout << res << endl;
}

浙公网安备 33010602011771号