【CF 708C】Centroids
題目簡述
給定一棵 \(n\) 個點的樹,你可以刪除一條邊並增加一條邊,形成一棵新樹。
問每個點在進行這樣的操作後,是否可能成為新樹的重心。
\(1\le n\le4\cdot10^5\)
思路
要讓每個子樹大小都小於等於 \(\left\lfloor\dfrac {n} {2}\right\rfloor\),如果如果這個子樹本身就可以作為重心,就不用改變。
考慮如果本身不能作為重心,說明一定有且只有一個子樹大小大於 \(\left\lfloor\dfrac {n} {2}\right\rfloor\)。
我們一定是從這個子樹裡面選一個子樹接在當前的根上面,否則不是最優的。那麼就要從該子樹裡面找到一個小於等於 \(\left\lfloor\dfrac {n} {2}\right\rfloor\)
考慮如何快速求出這個值。
看上去就很像一個換根 DP。
先考慮 \(dp_u\) 為 \(u\) 子樹內內該值的大小。對於他的兒子 \(v\),有如下方程:
\[f(x)= \begin{cases} \max(siz_v)& siz_v\le\left\lfloor\dfrac {n} {2}\right\rfloor\\ \max(dp_v)& \text{otherwise} \end{cases} \]考慮換根,那我們要得到子樹外能移除最大子樹的大小。
考慮 \(dp'_v\) 如何得到。考慮 \(dp_u\) 的最佳轉移點,如果 \(dp_u\)
然後如果 \(v\) 是 \(u\) 最佳轉移點,那麼 \(dp'_v\) 可取值為 \(dp[u][1]\) 和 \(n-siz_u(n-siz_u<=\left\lfloor\dfrac {n} {2}\right\rfloor)\),否則可取值為 \(dp_{u,0}\) 和 \(n-siz_u(n-siz_u<=\left\lfloor\dfrac{n}{2}\right\rfloor)\)
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
struct edge {
int to,nxt;
} e[N<<1];
int n,cnt,head[N],siz[N],maxsiz[N],f[N][2],pos[N];
inline void add(int u,int v) {
e[++cnt].nxt=head[u],head[u]=cnt,e[cnt].to=v;
}
void dfs1(int u,int fa) {
siz[u]=1;
for(int i=head[u]; i; i=e[i].nxt) {
if(e[i].to==fa) {
continue;
}
dfs1(e[i].to,u),siz[u]+=siz[e[i].to];
int v=f[e[i].to][0];
if(siz[e[i].to]>siz[maxsiz[u]]) {
maxsiz[u]=e[i].to;
}
if(siz[e[i].to]<=n/2) {
v=siz[e[i].to];
}
if(f[u][0]<v) {
f[u][1]=f[u][0],f[u][0]=v,pos[u]=e[i].to;
} else if(f[u][1]<v) {
f[u][1]=v;
}
}
}
int ans[N],dp[N];
void dfs2(int u,int fa) {
ans[u]=1;
if(siz[maxsiz[u]]>n/2) {
ans[u]=(siz[maxsiz[u]]-f[maxsiz[u]][0]<=n/2);
} else if(n-siz[u]>n/2) {
ans[u]=(n-siz[u]-dp[u]<=n/2);
}
for(int i=head[u]; i; i=e[i].nxt) {
if(e[i].to==fa) {
continue;
}
int v=n-siz[u];
if(n-siz[u]>n/2) {
v=dp[u];
}
dp[e[i].to]=max(dp[e[i].to],v);
if(pos[u]==e[i].to) {
dp[e[i].to]=max(dp[e[i].to],f[u][1]);
} else {
dp[e[i].to]=max(dp[e[i].to],f[u][0]);
}
dfs2(e[i].to,u);
}
}
int main() {
scanf("%d",&n);
for(int i=1,u,v; i<=n-1; i++) {
scanf("%d %d",&u,&v),add(u,v),add(v,u);
}
dfs1(1,0),dfs2(1,0);
for(int i=1; i<=n; i++) {
printf("%d ",ans[i]);
}
return 0;
}
本文作者:AFewMoon,文章地址:https://www.cnblogs.com/AFewMoon/p/15485288.html
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。
限於本人水平,如果文章有表述不當之處,還請不吝賜教。