题解 CF1187E 【Tree Painting】
前言
您完全可以略过这段。
校内模拟赛的时候一位大奆佬搬了这题。
然后我切了。
个人认为这题是一道不错的换根dp,虽然略显套路。
题意简述
给定一棵有
第一次操作可以随意使一个结点染成黑色,之后每次操作可以使一个与黑色结点相邻的白色结点变成黑色。
每次操作可以获得的权值为被染成黑色的白色结点所在的白色连通块的结点数量。
求可以获得的最大权值。
感觉自己写的好绕。
题目分析与解答
不难发现只要选定了第一个被染色的结点,答案也就确定了。
所以有一个朴素做法就是以枚举每个结点为根,都做一次树形dp。
以某一结点为根,记
方程就是
其中
这部分比较好理解就不画图举例啦。
时间复杂度
然后就会自然而然地想到换根dp。
先考虑以任意一点为根,不妨记为
然后记
显然有
然后考虑
以样例为例:
我们假设当前以
考虑一下答案的组成。
首先考虑
然后考虑父亲方向,也就是图中红圈部分对
那么除了以
不要忘了加上
综合一下上述两种方向的贡献,可以得到:
推广到所有结点,就可以得到:
然后跑两遍 dfs 就愉快的解决啦。
Code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN = 200010;
int n;
struct edge{
int ne, to;
edge(int N=0, int T=0):ne(N),to(T){}
}e[MAXN<<1];
int fir[MAXN], num = 0;
inline void join(int a, int b)
{
e[++num] = edge(fir[a], b);
fir[a] = num;
}
ll siz[MAXN], f[MAXN], g[MAXN], ans = 0;
void dfs1(int u, int fa)
{
siz[u] = 1;
for(int i=fir[u];i;i=e[i].ne)
{
int v = e[i].to;
if(v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
f[u] += f[v];
}
f[u] += siz[u];
}
void dfs2(int u, int fa)
{
for(int i=fir[u];i;i=e[i].ne)
{
int v = e[i].to;
if(v == fa) continue;
g[v] = n - siz[v] + g[u] - siz[v];
dfs2(v, u);
}
}
int main()
{
// freopen("stars3.in","r",stdin);
// freopen("stars.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int a, b;
scanf("%d%d",&a,&b);
join(a, b);
join(b, a);
}
dfs1(1, 0);
g[1] = f[1];
dfs2(1, 0);
ll ans = 0;
for(int i=1;i<=n;i++)
ans = max(ans, g[i]);
printf("%lld\n",ans);
return 0;
}
似乎只有我时而大括号换行,时而不换行?!