08-08 题解
A - CF1420E
luogu 翻译更正
if he gives no more that k orders
对于至多 k 次操作, 题面没有翻译出来
思路
怎么算贡献?
贡献 (被保护)出现在 「处在任意两个不同的 0 的连续段的守卫」之间,而处于同一连续段的守卫之间没有贡献
设一共
则
考虑 DP
设
但是这样的空间复杂度是
尝试优化掉一维状态
最后一维是最好优化的, 我们只需要把状态的含义改为 「考虑到第
而原来的
所以
注意, 输出答案时要取前缀
代码
#include<bits/stdc++.h> using namespace std; const int N = 81, INF = 1e9; int n; int a[N], ps[N], cnt, cntzr; int dp[N][N * N][N], ans[N * N]; int Calc(int x){ return x * (x - 1) / 2; } signed main(){ cin >> n; for(int i = 1; i <= n; i++){ cin >> a[i]; if(a[i] == 1) ps[++cnt] = i; else ++cntzr; } int up = Calc(n); for(int i = 0; i <= n; i++){ for(int j = 0; j <= up; j++){ for(int k = 0; k <= n; k++){ dp[i][j][k] = INF; } } } for(int i = 1; i <= n; i++){ dp[i][abs(ps[1] - i)][1] = Calc(i - 1); for(int j = 0; j <= up; j++){ for(int k = 1; k <= i && k < cnt; k++){ if(dp[i][j][k] == INF) continue; int rem = cnt - k; for(int ii = i + 1; ii <= n - rem + 1; ii++){ int jj = j + abs(ps[k + 1] - ii); if(jj <= up){ dp[ii][jj][k + 1] = min(dp[ii][jj][k + 1], dp[i][j][k] + Calc(ii - i - 1)); } } } } } for(int i = 0; i <= up; i++){ ans[i] = Calc(cntzr); } for(int j = 0; j <= up; j++){ if(j) ans[j] = ans[j - 1]; for(int i = cnt; i <= n; i++){ if(dp[i][j][cnt] == INF) continue; ans[j] = min(ans[j], dp[i][j][cnt] + Calc(n - i)); } } for(int i = 0; i <= up; i++){ ans[i] = Calc(cntzr) - ans[i]; cout << ans[i] << " "; } cout << "\n"; }
B - AGC017D
吐槽 MX 给的题面有误
思路
首先, 父节点的每个子树都可以看成一个独立的游戏, 拆开, 发现形如 Nim 游戏
递归地拆开后, 发现只需要处理父节点只有一个儿子的情况
但是这个游戏比较特殊, 如果当前节点非根, 我们可以一步直接断掉他和根的连边, 结束这颗子树中的游戏
这一点在博弈状态的 DAG 上表示为: 每个非根节点都指向 (SG = 0)
因为 SG(i) 表示
代码
#include<bits/stdc++.h> #define int long long #define Pii pair <int, int> using namespace std; const int N = 1e6 + 10, MOD = 20, INF = 1e18; int n; int u, v, sg[N]; vector <int> e[N]; void Dfs(int x, int y){ for(auto i : e[x]){ if(i == y) continue; Dfs(i, x); sg[x] ^= (sg[i] + 1); } } signed main(){ cin >> n; for(int i = 1; i < n; i++){ cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } Dfs(1, 0); if(sg[1]){ cout << "Alice\n"; }else{ cout << "Bob\n"; } }
C - AGC052B
经典套路: 把边权转移到点权上
思路
设点权设为从根到他的路径权值的抑或和
这样你就发现了一些神奇的东西: 对于一条边
但是有特殊情况:根节点没有连向父亲的边, 不妨假想一条
因为题目中的操作不会改变点权集合, 所以如果
将
代码
#include<bits/stdc++.h> #define int long long #define Pii pair <int, int> using namespace std; const int N = 1e7 + 10, MOD = 998244353; const int Inv2 = (MOD + 1) / 2; int n; int u, v, wa, wb; int a[N], b[N]; struct Edge{ int v, wa, wb; }; vector <Edge> e[N]; void Dfs(int x, int y, int va, int vb){ a[x] = va, b[x] = vb; for(auto i : e[x]){ if(i.v == y) continue; Dfs(i.v, x, va ^ i.wa, vb ^ i.wb); } } signed main(){ cin >> n; for(int i = 1; i < n; i++){ cin >> u >> v >> wa >> wb; e[u].push_back({v, wa, wb}); e[v].push_back({u, wa, wb}); } Dfs(1, 0, 0, 0); int xr = 0; for(int i = 1; i <= n; i++){ xr ^= (a[i] ^ b[i]); } for(int i = 1; i <= n; i++){ b[i] ^= xr; } sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + n); for(int i = 1; i <= n; i++){ if(a[i] != b[i]){ cout << "NO\n"; return 0; } } cout << "YES\n"; }
D - CF1710E
二分图博弈, 鸽
E - AGC040C
经典套路:黑白染色
思路
假设我们已经有了一个串, 将他的奇数位染成黑色, 偶数位染成白色
对于
不妨把黑色位置的
那么只有当
方案数用组合数算一下就行
#include<bits/stdc++.h> #define int long long #define Pii pair <int, int> using namespace std; const int N = 1e7 + 10, MOD = 998244353; const int Inv2 = (MOD + 1) / 2; int n; int tot; int fac[N], inv[N]; void Init(){ int up = 1e7; fac[0] = fac[1] = 1; inv[0] = inv[1] = 1; for(int i = 2; i <= up; i++){ fac[i] = fac[i - 1] * i % MOD; inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD; } for(int i = 2; i <= up; i++){ inv[i] = inv[i - 1] * inv[i] % MOD; } } int C(int n, int m){ return fac[n] * inv[m] % MOD * inv[n - m] % MOD; } int Q_pow(int a, int b){ int ans = 1, p = a; while(b){ if(b & 1) ans = (ans * p) % MOD; b >>= 1; p = (p * p) % MOD; } return ans; } signed main(){ // freopen("1.in", "r", stdin); cin >> n; Init(); tot = Q_pow(3, n); // cout << tot << "tot\n"; int st = (n + 1) / 2 + 1; // cout << st << "st\n"; for(int i = st; i <= n; i++){ int num = C(n, i) * Q_pow(2, n - i) % MOD * 2 % MOD; // cout << num << " "; tot = (tot - num + MOD) % MOD; } cout << tot << "\n"; }
F - CF1642F
根号分治, 随机化都行(题解区还有刚上新的同学的做法)
随机化正确率的计算
如果模数是 20, 那么不冲突的概率为 $\frac{C_{15}{5}}{C_{20}{5}} \approx 0.19369195046439628483 $
那么我们做 20 次, 正确率就是
代码
#include<bits/stdc++.h> #define int long long #define Pii pair <int, int> using namespace std; const int N = 1e6 + 10, MOD = 20, INF = 1e18; int n, m, ans; int a[N][7], w[N], num[N]; int dp[1 << 21], id[N]; int cnt; unordered_map <int, int> rid; void Calc(){ random_shuffle(id + 1, id + 1 + cnt); int upp = (1 << 21) - 1; for(int i = 0; i <= upp; i++){ dp[i] = INF; } for(int i = 1; i <= n; i++){ num[i] = 0; for(int j = 1; j <= m; j++){ a[i][j] = id[a[i][j]]; num[i] |= (1 << (a[i][j] % MOD)); } dp[num[i]] = min(dp[num[i]], w[i]); } for(int i = 0; i <= 20; i++){ for(int j = 0; j <= upp; j++){ if((j >> i) & 1){ dp[j] = min(dp[j], dp[j ^ (1 << i)]); } } } for(int i = 1; i <= n; i++){ ans = min(ans, w[i] + dp[upp ^ num[i]]); } } signed main(){ srand(1ll * time(0) * time(0) % 998244353); cin >> n >> m; for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++){ cin >> a[i][j]; if(!rid[a[i][j]]) rid[a[i][j]] = ++cnt; a[i][j] = rid[a[i][j]]; } cin >> w[i]; } for(int i = 1; i <= cnt; i++){ id[i] = i; } ans = INF; for(int i = 1; i <= 20; i++){ Calc(); } if(ans == INF){ ans = -1; } cout << ans << "\n"; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话