【题解】Codeforces Round 1045 (Div. 2) Problem.B ~ Problem.E
B. Add 0 or K
问题描述
给定一个数组 aaa 和一个整数 kkk,对于每个元素 aia_iai,可以增加 ci×kc_i \times kci×k(其中 cic_ici 是整数,且 0≤ci≤k0 \leq c_i \leq k0≤ci≤k)。目标是使整个数组的最大公因数(GCD)大于 1。
方法:使所有元素成为 k+1k+1k+1 的倍数
一个有效的策略是让每个元素 aia_iai 操作后成为 k+1k+1k+1 的倍数(因为 k+1≥2k+1 \geq 2k+1≥2,如果所有元素都是 k+1k+1k+1 的倍数,则 GCD 至少为 k+1>1k+1 > 1k+1>1)。
关键观察
- 由于 k≡−1(modk+1)k \equiv -1 \pmod{k+1}k≡−1(modk+1)(因为 k+1≡0(modk+1)k + 1 \equiv 0 \pmod{k+1}k+1≡0(modk+1),所以 k≡−1k \equiv -1k≡−1),增加 kkk 会改变 aia_iai 对 k+1k+1k+1 取模的结果。
- 具体地,每增加一次 kkk,aia_iai 对 k+1k+1k+1 取模的余数减少 1:
(ai+k)mod(k+1)=(aimod(k+1)−1)mod(k+1)(a_i + k) \mod (k+1) = (a_i \mod (k+1) - 1) \mod (k+1) (ai+k)mod(k+1)=(aimod(k+1)−1)mod(k+1)
类似地,可以选择 d=k−1d = k-1d=k−1(假设 k>1k > 1k>1)但是需要特判
Code
#include<bits/stdc++.h>
#define int long longusing namespace std;void solve()
{int n,k;cin >> n >> k;vector<int> a(n+1);for(int i = 1; i <= n; i ++){cin >> a[i];}if(k == 1){for(int i = 1; i <= n; i ++){if(a[i] % 2 == 1) a[i] += k;}}else if(k == 2){for(int i = 1; i <= n; i ++){if(a[i] % 3 == 1){a[i] += 2;}else if(a[i] % 3 == 2){a[i] += 4;}}}else{for(int i = 1; i <= n; i ++){int t = a[i] % (k-1);a[i] += ((k-1)-t) * k;}} for(int i = 1; i <= n; i ++){cout << a[i] << ' ';}cout << '\n';
}signed main()
{ios::sync_with_stdio(false);cin.tie(0);int t = 1;cin >> t;while(t --){solve();}
}
C. Even Larger
1.问题转化
如果每一个偶数和左右两边奇数构成的子段满足要求,则任意子段都满足。因为所有子段都可以通过删减满足要求的子段变为长度为3的子段。
2.贪心
对于 i = 2的元素,使得它右边的元素变小,一定优于使它左边元素变小。因为 i = 4 可以使用后者,而 2 的前面已经不存在偶数了。
Code
#include<bits/stdc++.h>
#define int long longusing namespace std;void solve()
{int n;cin >> n;vector<int> a(n+1);for(int i = 1; i <= n; i ++){cin >> a[i];}int ans = 0;for(int i = 2; i <= n; i += 2){int t = a[i-1];if(i + 1 <= n) t += a[i+1];ans += max(t-a[i],0ll);if(i + 1 <= n) {a[i+1] -= max(t-a[i],0ll);a[i+1] = max(a[i+1],0ll);}}cout << ans << '\n';}signed main()
{ios::sync_with_stdio(false);cin.tie(0);int t = 1;cin >> t;while(t --){solve();}
}
D. Sliding Tree
1.问题转化
要使得树变为链,最优情况是以树的直径(什么是树的直径?)为基础展开。如果能使得一颗树的直径长度为n - 1,这颗树就是一条链。
2.操作方法
每一步操作至多使得树的直径增加1。答案有下界 n−1−L直径n - 1 - L_{直径}n−1−L直径。每一步选择直径上的一个度数大于2的点,或者说"产生分叉的点”,将直径移动到其分支上,可以使得直径增加1。
而直径没变成链之前,永远存在”产生分叉的点“。
3.求解第一个操作位置
首先把直径找出来,然后遍历直径找分叉点即可。
(如何找到直径?)
Code
#include<bits/stdc++.h>
#define int long longusing namespace std;void solve(){int n;cin >> n;vector<vector<int>> v(n+1);for(int i = 1; i <= n - 1; i ++){int x,y;cin >> x >> y;v[x].push_back(y),v[y].push_back(x);}if(n <= 2){cout << -1 << '\n';return;}vector<int> dist1(n+1,1e18);auto dfs1 = [&](auto &&self,int u,int p,int depth)->void{dist1[u] = depth + 1;for(int i : v[u]){if(i != p){self(self,i,u,dist1[u]);}}};dfs1(dfs1,1,-1,0);int p = max_element(dist1.begin()+1,dist1.end()) - dist1.begin();vector<int> dist2(n+1,-1),ne(n+1,1e18);auto dfs2 = [&](auto &&self,int u,int p) -> int{bool flag = false;for(int i : v[u]){if(i != p){flag = 1;int ret = self(self,i,u); if(ret + 1 > dist2[u]){ne[u] = i;dist2[u] = ret + 1;}}}if(!flag) {ne[u] = -1;dist2[u] = 1;}return dist2[u];};dfs2(dfs2,p,-1);int u = ne[p],last = p;while(u != -1){if(v[u].size() >= 3){for(int i : v[u]){if(i != ne[u] && i != last){cout << last << ' ' << u << ' ' << i << '\n';return;}}}last = u;u = ne[u];}cout << -1 << '\n';
}signed main(){ios::sync_with_stdio(false);cin.tie(0);int t = 1;cin >> t;while(t --){solve();}
}
E. Power Boxes
1.突破口
考虑n - 1和n 两个位置,能否用3次询问确定这两个位置的能量值?答案是肯定的,先throw第n-1个位置,如果弹1次,n-1位置是2,否则是1。通过交换n-1和n这两个位置,可以求出n。
2.推进
得到了 n - 1 和 n 的能量值,如何求n - 3 和n - 2呢?
定义 dist[i]dist[i]dist[i] 为球被扔到 iii 处后,继续弹跳的次数。
- 如果 dist[n]==dist[n−1]dist[n] == dist[n-1]dist[n]==dist[n−1],可以按照上文方法的throw第n-3个位置再交换,在3次内求得。
- 如果 dist[n]!=dist[n−1]dist[n] != dist[n-1]dist[n]!=dist[n−1] 则一定有 dist[n]−1==dist[n−1]dist[n] - 1== dist[n-1]dist[n]−1==dist[n−1],这是由于能量值的取值只能是1和2. 这种情况改成throw第n - 2个位置即可。
3.小情况(n为奇数)
n为奇数需要特殊处理n == 1的情况,方法有很多,代码中提供了一种。
Code
#include<bits/stdc++.h>
#define int long longusing namespace std;void solve(){int n;cin >> n;vector<int> a(n+6),dist(n+6);auto thr = [&](int x){cout << "throw " << x << endl;int ret;cin >> ret;return ret;};vector<int> history;auto swa = [&](int x){swap(a[x],a[x+1]);cout << "swap " << x << endl;history.push_back(x);};int tmp = thr(n-1);if(tmp == 1) a[n-1] = 2;else a[n-1] = 1;swa(n-1);tmp = thr(n-1);if(tmp == 1) a[n-1] = 2;else a[n-1] = 1;for(int i = n - 3; i >= 1; i -= 2){if(a[i+3] == 2) dist[i+3] = dist[i+3+2] + 1;else dist[i+3] = dist[i+3+1] + 1;if(a[i+2] == 2) dist[i+2] = dist[i+2+2] + 1;else dist[i+2] = dist[i+2+1] + 1;if(dist[i+2] == dist[i+3]){int tmp = thr(i);if(tmp == dist[i+2]+1){a[i] = 2;}else{a[i] = 1;}swa(i);tmp = thr(i);if(tmp == dist[i+2]+1){a[i] = 2;}else{a[i] = 1;}}else{int tmp = thr(i+1);if(tmp == dist[i+2] + 1){a[i+1] = 1;}else{a[i+1] = 2;}swa(i);tmp = thr(i+1);if(tmp == dist[i+2]+1){a[i+1] = 1;}else{a[i+1] = 2;}}}if(n & 1){if(a[3] == 2) dist[3] = dist[5] + 1;else dist[3] = dist[4] + 1;if(a[2] == 2) dist[2] = dist[4] + 1;else dist[2] = dist[3] + 1;if(dist[2] != dist[3]){int tmp = thr(1);if(tmp == dist[2] + 1) a[1] = 1;else a[1] = 2;}else{swa(1);int tmp = thr(2);if(tmp == dist[3] + 1) a[2] = 1;else a[2] = 2;}}while(history.size()){swap(a[history.back()],a[history.back()+1]);history.pop_back();}cout << "! ";for(int i = 1; i <= n; i ++){cout << a[i] << ' ';}cout << endl;
}signed main(){ios::sync_with_stdio(false);cin.tie(0);int t = 1;cin >> t;while(t --){solve();}
}