Codeforces Round #798 Div.2 C-E 题解

Problem C

传送门

Solution

看完题面不难想到一个贪心思路:

从根节点开始 DFS,每次截断节点数更多的子树,然后递归到另一棵子树继续向下计算。

看起来十分美好,但有一个问题:如果两棵子树大小一样,结构又不清楚,怎么办?

这个时候就珂以用动态规划来解决了。

dp(u) 表示 u 被感染后其子树内能被救下的最大节点数。

dp(u)=max(dp(son1)+size(son2)1,dp(son2)+size(son1)1)

其中 son1,son2u 的子节点,size(v) 表示以 v 为根的子树的大小。

那么我们跑一遍树形 DP 即可qwq

时间复杂度 O(N)

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

不难想到暴力思路:枚举每个点,求出黑点与该点的曼哈顿距离最大值并更新答案。

这样最差会达到 O(N2M2),考虑优化。

显然不是所有的黑点都能对答案产生贡献,经观察,只有最左上,左下,右上,右下的黑点能产生最大值。

那么我们找出这四个点就珂以了。

对于点 (x,y),如果它在左上,则 x+y 取值最小,右下则为 x+y 最大。

同理可得,如果在左下则 xy 值最小,右上则 xy 值最大。

顺着这个思路,找到那四个点,再进行暴力枚举即可。

时间复杂度 O(NM)

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。

首先要注意,0 是始终无法连边的,所以所有的 0 开始时要先加上 1

下面就是神奇的结论:在处理了 0 的基础上,答案至多会再 +2

证明:

首先发现奇数全部有连边,零零散散的都是偶数。

设偶数中 lowbit 的最大值出现的位置为 ap1,ap2apk

  • 如果 k=1,那么我们只要令 ap1ap11,那么它也会变成奇数,同时它的二进制位包含了所有偶数的 lowbit 值,所以它与偶数间也有连边,整个图联通。

  • 如果 k>1,那么随便找两个位置 i,j(ij),令 apiapi1,apjapj+1。那么所有 pi,pj 这两个位置都与奇数有了连边,且 (pi,pj) 也连上了边,同时 lowbit 值低于最大值的所有点都与 pi 有连边,lowbit 值等于最大值的所有点都与 pj 连边,又因 (pi,pj) 有连边,所以整个图都连通。

综上,我们至多需两次操作。证毕。

据此,直接针对除处理 0 以外的操作数为 0,1,2 三种情况进行计算即可。

时间复杂度 O(N2logN)。(计入了 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;
}

完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*

posted @   ImALAS  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示