【做题笔记】板刷 CodeForces

news/2024/10/18 15:31:22/文章来源:https://www.cnblogs.com/Daniel-yao/p/18326784

CF1987D World is Mine

第一想法是贪心的决策,考虑到是博弈论,每一轮决策肯定都是最优的。显然贪心做法假掉。

发现问题具有最优子结构与后效性,考虑 dp。

\(a_i\) 数组排序,将相同元素打包成块,块长为 \(b_{a_i}\)。设 \(f_{i,j}\) 表示以第 \(i\) 个块结尾,剩余决策数为 \(j\) 的最优选择答案。

  • 若当前回合的剩余决策数立马用掉,不消除答案贡献,即为 \(f_{i,j}=f_{i-1,j}+1\)

  • 若当前回合的剩余决策数留下,不消除答案贡献,即为 \(f_{i,j}=f_{i-1,j-1}+1\)

  • 若当前回合的剩余决策数用掉 \(b_{a_i}\) 来消除答案贡献,即为 \(f_{i,j} = f_{i-1,j + b_{a_i}}\)

三种决策取最小即可。

由于博弈论的特殊性:双方在相同回合数时的总操作数相同,所以最后肯定不会剩余下决策数。所以答案为 \(f_{n,0}\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e17using namespace std;const int N = 5e3 + 10;int T, n, a[N], b[N], f[N][N];void solve() {int ans = 0, num = 0;cin >> n;For(i,1,n) b[i] = 0;For(i,0,n) For(j,0,n) f[i][j] = inf;For(i,1,n) cin >> a[i], b[a[i]]++;sort(a + 1, a + n + 1);int tot = unique(a + 1, a + n + 1) - a - 1;n = tot;f[0][0] = 0;For(i,1,n) {For(j,0,i) {if(j != 0) f[i][j] = min(f[i-1][j], f[i-1][j-1]) + 1;else f[i][0] = f[i-1][0] + 1;if(b[a[i]] + j <= i-1) f[i][j] = min(f[i][j], f[i-1][j + b[a[i]]]);}}cout << f[n][0] << '\n';return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

时间复杂度 \(O(Tn^2)\),记得多测清空。

CF1986D Mathematical Problem

考虑搜索,\(O(T2^n)\) 显然过不了。

先枚举相邻合并段,分析答案贡献的情况:先默认全加,发现答案上限为 \(19\times9 + 99=261\)。然后将一部分序列改为乘法操作。

\(f_i\) 表示前 \(i\) 个数组成的序列的最小答案,初始设为 \([1,i]\) 的数值和。向前枚举 \(j\),考虑将 \([j+1,i]\) 这一段改为乘法操作,于是就有 \(f[i]=\min\limits_{j=0}^{i-1}f[j]+\prod\limits_{k=j+1}^{i}a_k\),显然后半部分可以预处理,于是就能做 \(O(n^3)\) 的动态规划了。

因为乘法运算结果很大,所以当答案超过 \(261\) 时,就取 \(261\) 即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define mod 262using namespace std;const int N = 25;char a[N];int T, n, num[N], sum[N], dp[N], ans = 261, fuck_you_code_bug_tot;void solve() {cin >> n;int len = n-1, cnt0 = 0;ans = 261;For(i,1,n) cin >> a[i];For(i,1,n-1) {memset(num, 0, sizeof num);For(j,1,i-1) num[j] = a[j] - '0', sum[j] = sum[j-1] + num[j];num[i] = (a[i] - '0') * 10 + (a[i+1] - '0');sum[i] = sum[i-1] + num[i];For(j,i+2,n) num[j-1] = a[j] - '0', sum[j-1] = sum[j-2] + num[j-1];For(j,1,len) {cnt0 += (num[j] == 0);dp[j] = sum[j];int mul = num[j];FOR(k,j-1,0) {dp[j] = min(dp[j], dp[k] + mul);mul *= num[k];if(dp[j] > 261 || mul > 261) {break;} }}ans = min({ans, dp[len], (cnt0 > 0ll ? 0ll : 261ll)});}cout << ans << '\n';return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1983D Swap Dilemma

可以观察到在操作数无限的情况下,大步换和两两换效果相同。可以考虑 \(a\) 数组最后两个数 \([n-1,n]\) 两两不停交换,\(b\) 数组同时冒泡到 \(a\) 数组的对应位置,然后看 \([n,n-1]\) 段的 \(a,b\) 数组是否相同。

发现 \(a,b\) 交换次数相同,然后就可以想到 \(a,b\) 两者逆序对奇偶性相同,则能有 \(a,b\) 相同的机会。可以这样想:\(a,b\) 数组排序后,\(a,b\) 数组一定相同。此时 \(a,b\) 数组的交换次数同奇同偶时,操作结果同步。

用树状数组算分别算逆序对比较奇偶性即可,时间复杂度 \(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 2e5 + 10using namespace std;const int N = 4e5 + 10;int T, n, a[N], b[N], p[N], t[N];int lb(int x) {return x & -x;
}int ask(int x) {int ans = 0;for (int i = x; i; i -= lb(i)) {ans += t[i];}return ans;
}void upd(int x, int k) {for (int i = x; i <= inf; i += lb(i)) {t[i] += k;}
}void solve() {vector<int> v1, v2;int ans1 = 0, ans2 = 0;cin >> n;For(i,1,n) cin >> a[i], p[i] = i, v1.push_back(a[i]);For(i,1,n) cin >> b[i], v2.push_back(b[i]);sort(v1.begin(), v1.end());sort(v2.begin(), v2.end());for (int i = 0; i < v1.size(); ++i) {if(v1[i] != v2[i]) {puts("NO");return ;}}For(i,1,n) {ans1 += ask(a[i]);upd(a[i], 1); }For(i,1,n) upd(a[i], -1);For(i,1,n) {ans2 += ask(b[i]);upd(b[i], 1);}For(i,1,n) upd(b[i], -1);if((ans1 & 1) == (ans2 & 1)) {puts("YES");} else {puts("NO");}return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1986E Beautiful Array

考虑假设两数 \(x,y\) 能在加 \(k\) 的操作后相同,则 \(x-y\equiv 0\pmod{k}\),可以推得 \(x\equiv y\pmod{k}\)。可以想到按 \(a_i \bmod k\) 排序,同时比较 \(a_i\) 的值,保证 \(a_i\) 升序。

定义“一组”的概念为 \(a_i\bmod k\) 相同的 \(a_i\) 组成的升序序列。

对于一组来说,相邻两项凑一起算贡献肯定最优,自然推出了组长为偶数的情况,便是两两配对贡献。

考虑奇数组的情况:先枚举中间点 \(a_{mid}\),此数对答案无贡献,然后从左至右两两配对贡献。这里因为要枚举中间点,所以暴力的时间复杂度为 \(O(n^2)\)。可以考虑答案连续的贡献情况,每次抵消上一次的贡献,并将本次的贡献加上,每次取最小即可。(注意答案贡献的正负情况,自行推理)。

\(n\) 为偶数和奇数的情况来做:

  • \(n\) 为偶数时,若出现奇数组则无解,否则分组算贡献累加即可。

  • \(n\) 为奇数时,若出现两组以上的奇数组则无解,否则分组算贡献累加,奇数组单独处理。

总时间复杂度:\(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e11using namespace std;const int N = 2e5 + 10;int T, n, k, a[N];void solve() {int ans = 0;cin >> n >> k;For(i,1,n) cin >> a[i];sort(a + 1, a + n + 1, [](int x, int y){return (x % k == y % k ? x < y : x % k < y % k);});// For(i,1,n) cerr << a[i] << ' ';// cerr << '\n';if(n & 1) {int r = 1, l = 1, sum = 0, f = 0, Min = inf;vector<int> v;int cnt = 1;For(i,1,n) {if(a[i] % k == a[i+1] % k && i < n) cnt++, v.push_back(a[i]);else {if(cnt & 1) {v.push_back(a[i]);f++; r = i, l = r - v.size() + 1;} else vector<int>().swap(v);cnt = 1;}}for (int i = 1; i <= l-1; i += 2) ans += a[i+1] - a[i];for (int i = r+1; i <= n; i += 2) ans += a[i+1] - a[i];For(i,l,r) sum += (((i-l+1) & 1) ? 1 : -1) * a[i];Min = min(Min, sum - a[l]);For(i,l+1,r) {if(!((i-l+1) & 1)) {sum -= a[i-1] * 2;sum += a[i] * 2;}Min = min(Min, sum - a[i]);}if(f > 1) {cout << "-1\n";return ;}cout << (ans + Min) / k << '\n';} else {int cnt = 1;For(i,1,n) {if(a[i] % k == a[i+1] % k && i < n) {cnt++;} else {if(cnt & 1) {cout << "-1\n";return ;}cnt = 1;}}for (int i = 1; i <= n; i += 2) {ans += a[i+1] - a[i];}cout << ans / k << '\n';}return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1986F Non-academic Problem

考虑快速计算贡献:设将图分为大小为 \(n_1,n_2\) 的两部分,显然有 \(n_1+n_2=n\)。则当前方案贡献为 \(\min(\frac{n_1^2-n_1}{2},\frac{n_2^2-n_2}{2})\)

由于要最小化路径树,且只能删去一条边。所以最好的情况是将图尽可能分为两部分,所以满足删除条件的边就是割边。

先缩边双联通分量,然后枚举割边(缩成树后跑 dfs)。然后计算删去该割边的贡献即可,最后取最小的贡献作为答案。

总时间复杂度 \(O(n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e17using namespace std;const int N = 2e5 + 10;struct Node {int v, nx, u;
} e[N], E[N]; int T, n, m, h[N], H[N], dfn[N], low[N], cut[N], dcc[N], Col, tot = 1, idx, Idx = 1, siz[N];void add(int u, int v) {e[++tot] = (Node){v, h[u], u};h[u] = tot;
}void Add(int u, int v) {E[++Idx] = (Node){v, H[u], u};H[u] = Idx;
}void tarjan(int x, int fa) {dfn[x] = low[x] = ++idx;for (int i = h[x]; i; i = e[i].nx) {int y = e[i].v;if(!dfn[y]) {tarjan(y, x);low[x] = min(low[x], low[y]);if(low[y] > dfn[x]) cut[i] = cut[i ^ 1] = 1;} else if(y != fa) {low[x] = min(low[x], dfn[y]);}}
}void dfs(int x, int cnt) {dcc[x] = cnt;siz[cnt]++;for (int i = h[x]; i; i = e[i].nx) {int y = e[i].v;if(dcc[y] || cut[i]) continue;dfs(y, cnt);}
}void Dfs(int x, int fa) {for (int i = H[x]; i; i = E[i].nx) {int y = E[i].v;if(y == fa) continue;Dfs(y, x);siz[x] += siz[y];}
}void solve() {int ans = inf;Col = idx = 0;Idx = tot = 1;memset(cut, 0, sizeof cut);For(i,1,n) {h[i] = H[i] = 0;dfn[i] = low[i] = dcc[i] = siz[i] = 0;}cin >> n >> m;For(i,1,m) {int u, v; cin >> u >> v;add(u, v), add(v, u);}tarjan(1, 1);For(i,1,n) {if(!dcc[i]) dfs(i, ++Col);}For(i,1,n) {for (int j = h[i]; j; j = e[j].nx) {int y = e[j].v;if(dcc[i] != dcc[y]) Add(dcc[i], dcc[y]);} }Dfs(1, 0);For(i,1,n) {ans = min(ans, (n * n - n - (2 * siz[i] * (n - siz[i]))) / 2);}cout << ans << endl;return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1982D Beauty of the mountains

单独考虑每一个山的贡献很难做,不妨以 \(k\times k\) 的矩形为基础计算贡献。

我们发现每在 \(k\times k\) 的矩阵内加上 \(x\) 的贡献,钦定有雪与无雪的山分别作正、负贡献,记为 \(b_i\)。操作前的贡献为 \(sum\),有雪的山总高度和无雪的格子的总高度相等其等价于判断 \(\sum x_ib_i+sum=0\),即为 \(\sum x_ib_i=-sum\)

这就是一个 \(n\) 阶裴蜀定理,若 \(-sum\)\(\gcd{x_i}\) 的倍数,则 \(x_i\) 有整数解。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)using namespace std;const int N = 505;int T, n, m, k, a[N][N], s[N][N], Ans;bool b[N][N];int ask(int x1, int y1, int x2, int y2) {return s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
}void solve() {Ans = 0;int sum = 0;cin >> n >> m >> k;memset(s, 0, sizeof s);For(i,1,n) For(j,1,m) cin >> a[i][j];For(i,1,n) {For(j,1,m) {char c; cin >> c;b[i][j] = c - '0';}} For(i,1,n) {For(j,1,m) {sum += (b[i][j] ? a[i][j] : -a[i][j]);int F = (b[i][j] == 1 ? 1 : -1);s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + F;}}For(i,1,n-k+1) {For(j,1,m-k+1) {int k1 = ask(i, j, i+k-1, j+k-1);Ans = __gcd(Ans, k1);}}if(sum == 0) {puts("YES");return ;}if(Ans == 0) {puts("NO");return ;}if(sum % Ans == 0) puts("YES");else puts("NO");return ;
}signed main() {// ios::sync_with_stdio(0);// cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1974D Ingenuity-2

相同的操作下,操作顺序对最终的位置无影响。

机器人的位置初始都为 \((0,0)\),所以只要操作同步即可。

此外注意到 \(N\)\(S\)\(E\)\(W\) 发生在同一个机器人上时,可以起到抵消的作用。

我们不希望只有一个机器人操作,所以让 R 机器人进行一次抵消操作,后面的抵消操作全部分配给 H。

其他的操作平均、同等分配即可。若无法分则判断无解。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)using namespace std;const int N = 2e5 + 10, M = 200;int T, n, b[M];char a[N], ans[N];void solve() {memset(b, 0, sizeof b);memset(ans, 0, sizeof ans);cin >> n >> (a + 1);if(n & 1) {cout << "NO\n";return ;}For(i,1,n) b[a[i]]++;int k1 = min(b['N'], b['S']), k2 = min(b['E'], b['W']);b['N'] -= k1, b['S'] -= k1, b['E'] -= k2, b['W'] -= k2;int K1 = k1;bool f1, f2; f1 = f2 = 0;for (int i = 1; i <= n && (k1 >= 0 || K1 >= 0); ++i) {if(a[i] == 'N' && k1 > 0) {if(f1) ans[i] = 'H';else if(!f1) ans[i] = 'R', f1 = 1;k1--; }if(a[i] == 'S' && K1 > 0) {if(f2) ans[i] = 'H';else if(!f2) ans[i] = 'R', f2 = 1;K1--; }}K1 = k2;for (int i = 1; i <= n && (k2 >= 0 || K1 >= 0); ++i) {if(a[i] == 'E' && k2 > 0) {if(f1) ans[i] = 'H';else if(!f1) ans[i] = 'R', f1 = 1;k2--; }if(a[i] == 'W' && K1 > 0) {if(f2) ans[i] = 'H';else if(!f2) ans[i] = 'R', f2 = 1;K1--; }}bool f = 1;for (int i : {'N', 'S', 'E', 'W'}) {f &= (b[i] == 0 && n <= 2);if(b[i] & 1) {cout << "NO" << '\n';return ;}}if(f) {cout << "NO" << '\n';return ;}For(i,1,n) {if((b[a[i]] & 1) && !ans[i]) ans[i] = 'R';else if(!(b[a[i]] & 1) && !ans[i]) ans[i] = 'H';b[a[i]]--;}For(i,1,n) cout << ans[i];cout << '\n';return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1988D The Omnipotent Monster Killer

第一眼有点像最大独立集,可以考虑往这方面想。

由于有了回合的限制,所以在 dp 时要多加一维来除去回合的后效性。设 \(dp_{x,y}\) 表示第 \(x\) 个点在 \(y\) 回合被选中带来的最小贡献。很显然初始状态 \(dp_{x,y}=a_x\times y\)

转移也很显然,父亲和儿子不能在同一回合被选中,所以只需要在 dfs 中枚举所有回合,然后把所有不同回合的儿子状态取最小贡献,转移至父亲即可。

考虑回合数,因为每一次选点会将树拆成森林,然后每一个小树又会减半,继续分。所以可以证明回合数在 \(\log n\) 量级。

所以转移与计算贡献加上总时间复杂度 \(O(n \log^2 n)\),可以通过此题。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e18using namespace std;const int N = 3e5 + 10;struct Node {int v, nx;
} e[N << 1];int T, n, a[N], h[N], dp[N][25], tot, m, sum, ans = inf;void add(int u, int v) {e[++tot] = (Node){v, h[u]};h[u] = tot;
}void dfs(int x, int fa) {for (int i = h[x]; i; i = e[i].nx) {int y = e[i].v;if(y == fa) continue;dfs(y, x);}if(x == 1) return ;For(i,1,m) {int num = inf;For(j,1,m) {if(i == j) continue;num = min(num, dp[x][j]);}dp[fa][i] += (num == inf ? 0 : num); }
}void solve() {ans = inf; sum = tot = 0;For(i,1,n) h[i] = 0;cin >> n;For(i,1,n) cin >> a[i], sum += a[i];For(i,1,n-1) {int u, v;cin >> u >> v;add(u, v), add(v, u);}m = (int)ceil(log2(n))+1;For(i,1,n) For(j,1,m) dp[i][j] = a[i] * j;dfs(1, 0);For(i,1,m) ans = min(ans, dp[1][i]);cout << ans << '\n';return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1980D GCD-sequence

考虑在原数列中删去一个数 \(a_i\)\(b\) 数列会怎样改变。

因为 \(b_i=\gcd(a_i,a_{i+1})\),所以当 \(a_i\) 删除时,会影响到 \(b_i,b_{i-1}\)

先预处理出 \(l,r\) 表示从左到右、从右到左递增序列的区间,即 \([1,l],[r,n]\) 为递增区间。

然后暴力枚举删除哪个 \(a_i\),然后将 \(a_i\) 影响到的 \(b_i,b_{i-1}\) 删去,插入新贡献 \(c=\gcd(a_{i-1}, a_{i+1})\)。最后检查新贡献 \(c\) 在数列中是否能让数列满足递增,即修改的区间 \([i-2,i+1]\) 能否拼接上 \([1,l],[r,n]\),并且 \(b_{i-2} \leq c \leq b_{i+1}\)

总时间复杂度 \(O(n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define inf 1e8using namespace std;const int N = 2e5 + 10;int T, n, a[N], b[N];void solve() {cin >> n;For(i,1,n) cin >> a[i];a[n+1] = a[0] = 0;For(i,1,n-1) b[i] = __gcd(a[i], a[i+1]);b[n] = inf;int l = 1, r = n-1;while(l <= n-1 && b[l+1] >= b[l]) l++;while(r >= 1 && b[r-1] <= b[r]) r--;if(l == n-2 || r == 2) {puts("YES"); return ;}For(i,1,n) {int New = __gcd(a[i-1], a[i+1]);if(New >= b[max(0ll, i-2)] && New <= b[min(n, i+1)] && l >= i - 2 && r <= i + 1) {puts("YES"); return ;}}puts("NO");return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1401F Reverse and Swap

操作 \(1,4\) 是很常规的单点修改,区间查询操作,重点是在维护操作 \(2,3\) 的旋转。

可以发现对于操作 \(2,3\) 都很难在节点上打上标记,如果暴力去打会因为结点数太多而时间复杂度直线上升。又观察到层数不超过 \(19\),且建出的线段树是一棵完全二叉树。所以我们可以将标记打到层上。由于同层有多个结点,所以标记用完不能删除,但是不删除有可能会被旋转两次以上。所以我们利用标记永久化的思想:遇到标记反向走(应走左子树去走右子树,应走右子树去走左子树)来模拟线段树结点的旋转。

记得操作 \(3\) 的标记要往上打一层,因为需要旋转的是这一层本身。

总时间复杂度 \(O(2^n+qn)\)\(n\leq 18\),包可过。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)using namespace std;const int N = 20, M = 1e7 + 10;struct Node {int ls, rs;int sum;
} t[M];int n, q, idx, root, rev[N];void pushup(int p) {t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum;
}void build(int &p, int l, int r) {p = ++idx;if(l == r) {cin >> t[p].sum;return ;}int mid = l + r >> 1;build(t[p].ls, l, mid);build(t[p].rs, mid + 1, r);pushup(p);
}void upd(int p, int l, int r, int x, int k, int level) {if(l == r) {t[p].sum = k;return ;}int ls = t[p].ls, rs = t[p].rs;if(rev[level]) swap(ls, rs);int mid = l + r >> 1;if(x <= mid) upd(ls, l, mid, x, k, level-1);else upd(rs, mid + 1, r, x, k, level-1);pushup(p);
}int ask(int p, int l, int r, int L, int R, int level) {if(L <= l && r <= R) {return t[p].sum;}int ls = t[p].ls, rs = t[p].rs;if(rev[level]) swap(ls, rs);int mid = l + r >> 1, ans = 0;if(L <= mid) ans += ask(ls, l, mid, L, R, level-1);if(R > mid) ans += ask(rs, mid + 1, r, L, R, level-1);return ans;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n >> q;build(root, 1, (1<<n));while(q--) {int op; cin >> op;if(op == 1) {int x, k;cin >> x >> k;upd(root, 1, (1<<n), x, k, n);} else if(op == 2) {int k; cin >> k;For(i,0,k) rev[i] ^= 1;} else if(op == 3) {int k; cin >> k;rev[k+1] ^= 1;} else {int l, r; cin >> l >> r;cout << ask(root, 1, (1<<n), l, r, n) << '\n';}}return 0;
}

CF1322C Instant Noodles

引理:在整数范围内,\(\gcd(x,y,x+y)=\gcd(x,y)\)

证明:可以找到一个整数 \(b\) 使得 \(b|x,b|y\),且记所有可行的 \(b\) 中最大的数为 \(c\),则有:

\[x\equiv y\equiv0\pmod{c} \]

因为有 \(x\equiv0\pmod{c}\),所以:

\[2x\equiv x+y\equiv x\equiv0\pmod{c} \]

所以 \(x+y\equiv0\pmod{c}\)

此时 \(c=\gcd(x,y)\),因此得证 \(\gcd(x,y,x+y)=\gcd(x,y)\)

有了这一点理论,我们发现本题可以看成是求 \(c_i\) 张成的线性空间中的最大公约数。

不妨将右部点看成左部点的映射,能观察到在右部点选择一个点等价于在左部点选择点集。贡献也能相应的算出。

由于右部点 \(c_i\) 可张成线性空间,所以求任取 \(c_i\) 的最大公约数和求所有 \(c_i\) 的最大公约数是等价的。

最后只要注意合并相同的集合,并合并贡献即可。

集合用 set,映射用 map,总时间复杂度 \(O(n \log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)using namespace std;const int N = 1e6 + 10;int T, n, m, ans, c[N];set<int> e[N];map<set<int>, int> mp;void solve() {ans = 0;map<set<int>, int>().swap(mp);For(i,1,n) set<int>().swap(e[i]);cin >> n >> m;For(i,1,n) cin >> c[i];For(i,1,m) {int u, v;cin >> u >> v;e[v].insert(u);}For(i,1,n) {if(e[i].size()) mp[e[i]] += c[i];}for (auto x : mp) {ans = __gcd(ans, x.second);}cout << ans << '\n';return ;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

CF1416B Make Them Equal

没有第一眼看出来解法。

先判断无解:由于每一次操作涉及 \(+x\cdot i\)\(-x\cdot i\),所以序列的总和不变。因此最后想要每个数都一样必须让这 \(n\) 个数的平均数填满 \(n\) 个空,所以总和 \(sum\) 不能被 \(n\) 整除则无解。

然后发现 \(i=1\) 的自由度是很高的(因为 \(1\) 是所有自然数的因数,可以很轻松的被放与拿)。所以大致的思路就是每一次将 \(a_i\) 转移至 \(a_1\) 上,然后再平均分配给每一个数即可。

转移的过程很简单,就是对于 \(a_i\) 先凑 \(i\) 的倍数,凑的时候直接拿 \(a_1\) 来用,凑完以后直接放回 \(a_1\)。如果 \(a_i\) 本身就是 \(i\) 的倍数了,直接放到 \(a_1\) 即可。

\(a_1\) 是一定够补的,因为第 \(i\) 个空最多要补 \(i-1\),而最坏的情况就是前 \(i-1\) 个数都是 \(1\),而它们现在都放在 \(a_1\) 了,也就是正好能够补上 \(i\) 的空。

发现最后操作数不会超过 \(3(n-1)\),总时间复杂度 \(O(n)\),符合题意,包能过。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)using namespace std;const int N = 1e5 + 10;struct Ans {int i, j, x;
};int T, n, a[N];void solve() {vector<Ans> ans;int sum = 0, cnt = 0;cin >> n;For(i,1,n) cin >> a[i], sum += a[i];if(sum % n != 0) {cout << "-1\n";return ; }sum /= n;For(i,2,n) {if(a[i] % i == 0) {ans.push_back((Ans){i, 1, a[i] / i});continue;}int k = (int)(ceil(1.0 * a[i] / i) * i);ans.push_back((Ans){1, i, k - a[i]});ans.push_back((Ans){i, 1, k / i});}For(i,2,n) ans.push_back((Ans){1, i, sum});cout << ans.size() << '\n';for (int i = 0; i < ans.size(); ++i) cout << ans[i].i << ' ' << ans[i].j << ' ' << ans[i].x << '\n';
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while(T--) solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/772652.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01-从WordCount程序理解Spark术语及术语间的关系

1. 应用程序(Application) 通过下面的代码设置应用程序名称,设置后再UI中可以看到相应的名称。 //1.设置Application的名称 val conf = new SparkConf() conf.setAppName("WordCount") conf.setMaster("local")2. Job Job由scala的执行算子生成,每个执…

leetcode-5

题目: 给你一个字符串 s,找到 s 中最长的 回文子串 示例 1:输入:s = "babad"  输出:"bab"  解释:"aba" 同样是符合题意的答案。 示例 2:输入:s = "cbbd"   输出:"bb" 提示: 1 <= s.length <= 1000…

Uniapp 之手写签名

一、效果图二、代码示例 qianming.jsexport const qianming = {data() {return {windowWidth: 0,pixelRatio: 0,context: null,points: [],oldPoints: [],qm_width: 280,qm_height: 120,qm_img: }},methods: {qm_start() {const systemInfo = uni.getSystemInfoSync()this.wind…

代码随想录day11 || 150 逆表达式求值 239 滑动窗口最大值 347 前k最高频元素

150 逆波兰表达式计算func evalRPN(tokens []string) int {// 自己想是真的想不出来,看了视频之后有了思路// 本质上逻辑就是遇到数字入栈,遇到运算符号 出栈两个元素然后计算再入栈,最终就是计算结果stack := Constructor()for _, val := range tokens{// 如果数字入栈if i…

计算机组成与体系结构-校验码

奇偶校验码 奇偶校验是一种简单有效的校验方法,这种方法通过在编码中增加一位校验位来使编码中1的个数为奇数(奇校验)或者为偶数(偶校验),只能发现奇数个数据位 出错的情况.循环冗余校验码 CRC(Cyclic RedundancyCheck)循环冗余校验是一种常用的错误检测技术,用于在数据传输…

CAD快捷键命令大全(最全)

勇者无惧,强者无敌。

电影《抓娃娃》迅雷/百度云下载[超清版BT种子][MP4/2.89GB]分享

电影《抓娃娃》是一部由闫非、彭大魔联合执导,沈腾、马丽领衔主演的喜剧电影。该片于2024年7月16日在中国大陆正式上映,以其独特的剧情设定和深刻的主题探讨,迅速吸引了广大观众的关注。影片不仅延续了“沈马组合”一贯的幽默风格,更在喜剧外壳下包裹了深刻的教育主题,让人…

PWM波形生成

背景 方法 定时器 (1)高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11的时钟来源是APB2总线 (2)通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7的时钟来源是APB1总线 (3)当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM…

ComfyUI插件:ComfyUI Impact 节点(一)

前言: 学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器、细节强化器、预览桥、通配符、Hook、图片发送器、图片接收器等等。通过这些节点的组合运用,我们可以实现的工作有很多,例如自动人脸检测和优化修…

ComfyUI进阶:Comfyroll节点 (最终篇)+应用实例

前言: 学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业的图像生成与编辑工具。借助这些节点,用户可以在静态图像的精细调整和动态动画的复杂构建方面进行深入探索。Comfyroll 的节点设计简洁易用,功能…

松灵机器人scout mini小车 自主导航(4)——运行lio-sam建图

松灵机器人Scout mini小车运行lio-sam 在之前的工作中,我们已经实现了用小车搭载传感器,采用gmapping建图和navigation导航实现小车在2D环境中自主导航,但是实际我们采用的激光雷达多为三维激光雷达。因此决定采用lio-sam来建图。具体操作步骤如下。 1.下载雷达仿真 1.1下载…

[题解]P2672 [NOIP2015 普及组] 推销员

P2672 [NOIP2015 普及组] 推销员 为了便于操作,将住户信息按疲劳值从大到小排序。 那么对于选\(X\)个住户,有\(2\)种情况:选疲劳值前\(X\)大的住户,答案即为\(\sum\limits_{i=1}^X a[i] + 2\times \max\limits_{i=1}^X s[i]\)。 选疲劳值前\(X-1\)大的住户,然后在剩下的住…