Codeforces Round #798 Div.2 C-E 题解
Problem C
Solution
看完题面不难想到一个贪心思路:
从根节点开始 DFS,每次截断节点数更多的子树,然后递归到另一棵子树继续向下计算。
看起来十分美好,但有一个问题:如果两棵子树大小一样,结构又不清楚,怎么办?
这个时候就珂以用动态规划来解决了。
设 表示 被感染后其子树内能被救下的最大节点数。
则 。
其中 为 的子节点, 表示以 为根的子树的大小。
那么我们跑一遍树形 DP 即可qwq
时间复杂度 。
Code
#include <bits/stdc++.h> #define pb emplace_back using namespace std; const int maxn = 5e5 + 5; vector<int> g[maxn]; vector<int> son; int sz[maxn],n,dp[maxn]; int ans; void dfs(int u,int fa) { sz[u] = 1; for(auto v : g[u]) { if(v == fa)continue ; dfs(v , u); sz[u] += sz[v]; } for(auto v : g[u]) { if(v != fa)son.pb(v); } if(son.size() == 0)dp[u] = 0; else if(son.size() == 1)dp[u] = sz[son[0]] - 1; else dp[u] = max(dp[son[0]] + sz[son[1]] - 1 , dp[son[1]] + sz[son[0]] - 1); son.clear(); return ; } void work() { ans = 0; scanf("%d",&n); for(int i = 1;i <= n;++ i)sz[i] = 0,g[i].clear(); for(int i = 1;i < n;++ i) { int x,y; scanf("%d%d",&x,&y); g[x].pb(y); g[y].pb(x); } dfs(1 , 0); printf("%d\n",dp[1]); return ; } int main() { int T; scanf("%d",&T); while(T --)work(); return 0; }
Problem D
Solution
不难想到暴力思路:枚举每个点,求出黑点与该点的曼哈顿距离最大值并更新答案。
这样最差会达到 ,考虑优化。
显然不是所有的黑点都能对答案产生贡献,经观察,只有最左上,左下,右上,右下的黑点能产生最大值。
那么我们找出这四个点就珂以了。
对于点 ,如果它在左上,则 取值最小,右下则为 最大。
同理可得,如果在左下则 值最小,右上则 值最大。
顺着这个思路,找到那四个点,再进行暴力枚举即可。
时间复杂度 。
Code
#include <bits/stdc++.h> using namespace std; const int maxn = 1e3 + 5; struct node { int x,y; node() { x = y = 0; } node(int x,int y):x(x),y(y){} }a[5]; bool vis[5]; int n,m; void work() { scanf("%d%d",&n,&m); for(int i = 1;i <= 4;++ i)a[i] = node(0 , 0),vis[i] = false; for(int i = 1;i <= n;++ i) { for(int j = 1;j <= m;++ j) { char c; scanf(" %c",&c); if(c != 'B')continue ; if(!vis[1]||i + j > a[1].x + a[1].y)a[1] = node(i , j),vis[1] = true; if(!vis[2]||i + j < a[2].x + a[2].y)a[2] = node(i , j),vis[2] = true; if(!vis[3]||i - j > a[3].x - a[3].y)a[3] = node(i , j),vis[3] = true; if(!vis[4]||i - j < a[4].x - a[4].y)a[4] = node(i , j),vis[4] = true; } } int x = 0,y = 0,tot = 0x3f3f3f3f; for(int i = 1;i <= n;++ i) { for(int j = 1;j <= m;++ j) { int cur = 0; for(int k = 1;k <= 4;++ k) { cur = max(cur , abs(a[k].x - i) + abs(a[k].y - j)); } if(cur < tot) { tot = cur; x = i; y = j; } } } printf("%d %d\n",x,y); return ; } int main() { int T; scanf("%d",&T); while(T --)work(); return 0; }
Problem E
Solution
挺搞的一道结论题。证明是我看了滴叉以及几位大佬的题解以后弄出来的QAQ。
首先要注意, 是始终无法连边的,所以所有的 开始时要先加上 。
下面就是神奇的结论:在处理了 的基础上,答案至多会再 。
证明:
首先发现奇数全部有连边,零零散散的都是偶数。
设偶数中 的最大值出现的位置为 。
-
如果 ,那么我们只要令 ,那么它也会变成奇数,同时它的二进制位包含了所有偶数的 值,所以它与偶数间也有连边,整个图联通。
-
如果 ,那么随便找两个位置 ,令 。那么所有 这两个位置都与奇数有了连边,且 也连上了边,同时 值低于最大值的所有点都与 有连边, 值等于最大值的所有点都与 连边,又因 有连边,所以整个图都连通。
综上,我们至多需两次操作。证毕。
据此,直接针对除处理 以外的操作数为 三种情况进行计算即可。
时间复杂度 。(计入了 dsu
的时间复杂度)
Code
#include <bits/stdc++.h> using namespace std; const int maxn = 2e3 + 5; int n,a[maxn],s; int lowbit(int x) { return x & -x; } bool vis[maxn]; void dfs(int x) { if(vis[x])return ; s += vis[x] = true; for(int i = 1;i <= n;++ i) { if(i != x&&!vis[i]&&a[i] & a[x]) { dfs(i); } } return ; } int pre[maxn]; int find(int x) { return x == pre[x] ? x : pre[x] = find(pre[x]); } bool check() { for(int i = 1;i <= n;++ i)pre[i] = i; for(int k = 0;k < 30;++ k) { int fir = n; for(int i = 1;i < n;++ i) { if(a[i] >> k & 1) { fir = i; break ; } } for(int i = fir + 1;i <= n;++ i) if((a[i] >> k & 1)&&find(i) != find(fir))pre[find(i)] = find(fir); } for(int i = 2;i <= n;++ i) { if(find(i) != find(1))return false; } return true; } void work() { int maxl = 0,ans = 0; scanf("%d",&n); for(int i = 1;i <= n;++ i)scanf("%d",&a[i]),vis[i] = false; for(int i = 1;i <= n;++ i)ans += (!a[i]) ? ++ a[i] : 0,maxl = max(maxl , lowbit(a[i])); s = 0; dfs(1); if(s == n) { printf("%d\n",ans); for(int i = 1;i <= n;++ i) { printf("%d ",a[i]); } puts(""); return ; } for(int i = 1;i <= n;++ i) { -- a[i]; if(check()) { printf("%d\n",ans + 1); for(int k = 1;k <= n;++ k) { printf("%d ",a[k]); } puts(""); return ; } ++ a[i]; ++ a[i]; if(check()) { printf("%d\n",ans + 1); for(int k = 1;k <= n;++ k) { printf("%d ",a[k]); } puts(""); return ; } -- a[i]; } int cnt = 0; for(int i = 1;i <= n;++ i) { if(lowbit(a[i]) == maxl) { ++ cnt; if(cnt == 1)-- a[i]; else { ++ a[i]; break ; } } } printf("%d\n",ans + 2); for(int i = 1;i <= n;++ i)printf("%d ",a[i]); puts(""); return ; } int main() { int T; scanf("%d",&T); while(T --) { work(); } return 0; }
完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具