Codeforces Round #798 Div.2 C-E 题解
作者:互联网
Problem C
Solution
看完题面不难想到一个贪心思路:
从根节点开始 DFS,每次截断节点数更多的子树,然后递归到另一棵子树继续向下计算。
看起来十分美好,但有一个问题:如果两棵子树大小一样,结构又不清楚,怎么办?
这个时候就珂以用动态规划来解决了。
设 \(dp(u)\) 表示 \(u\) 被感染后其子树内能被救下的最大节点数。
则 \(dp(u) = \max(dp(son_1) + size(son_2)-1,dp(son_2)+size(son_1)-1)\)。
其中 \(son_1,son_2\) 为 \(u\) 的子节点,\(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(N^2M^2)\),考虑优化。
显然不是所有的黑点都能对答案产生贡献,经观察,只有最左上,左下,右上,右下的黑点能产生最大值。
那么我们找出这四个点就珂以了。
对于点 \((x,y)\),如果它在左上,则 \(x+y\) 取值最小,右下则为 \(x+y\) 最大。
同理可得,如果在左下则 \(x-y\) 值最小,右上则 \(x-y\) 值最大。
顺着这个思路,找到那四个点,再进行暴力枚举即可。
时间复杂度 \(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\)。
证明:
首先发现奇数全部有连边,零零散散的都是偶数。
设偶数中 \(\operatorname{lowbit}\) 的最大值出现的位置为 \(a_{p_1},a_{p_2}\ldots a_{p_k}\) 。
-
如果 \(k=1\),那么我们只要令 \(a_{p_1} \gets a_{p_1} -1\),那么它也会变成奇数,同时它的二进制位包含了所有偶数的 \(\operatorname{lowbit}\) 值,所以它与偶数间也有连边,整个图联通。
-
如果 \(k \gt 1\),那么随便找两个位置 \(i,j(i \neq j)\),令 \(a_{p_i} \gets a_{p_i}-1,a_{p_j}\gets a_{p_j}+1\)。那么所有 \(p_i,p_j\) 这两个位置都与奇数有了连边,且 \((p_i,p_j)\) 也连上了边,同时 \(\operatorname{lowbit}\) 值低于最大值的所有点都与 \(p_i\) 有连边,\(\operatorname{lowbit}\) 值等于最大值的所有点都与 \(p_j\) 连边,又因 \((p_i,p_j)\) 有连边,所以整个图都连通。
综上,我们至多需两次操作。证毕。
据此,直接针对除处理 \(0\) 以外的操作数为 \(0,1,2\) 三种情况进行计算即可。
时间复杂度 \(O(N^2\log N)\)。(计入了 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;
}
完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*
标签:node,int,题解,son,vis,maxn,Div.2,798,dp 来源: https://www.cnblogs.com/663B/p/16439019.html