題解[P5666樹的重心]
題意:給定一棵樹,求刪去每條邊後分裂成的兩棵樹的重心的編號和之和。
\(\text{Solution}\):
對每個點 \(x\) 分開討論,統計有多少條邊割掉後 \(x\) 能成為重心。
\(\text{Part 1}\) : 理論分析
假設讓 \(x\) 成為根,其最大子樹大小為 \(mx\) , 次大子樹大小為 \(mx1\)
第一種情況:不在最大子樹中割邊
這樣割後 \(x\) 的最大子樹為必為 \(mx\)
設割去一個子樹,其大小為 \(t\) ,則與 \(x\) 連通的樹大小為 \(n-t\)
則 \(x\) 能成為重心就有 \(mx \leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
第二種情況:在最大子樹中割邊
割後形成的最大子樹大小是 \(\max(mx1,mx-t)\)
所以有 \(\max(mx1,mx-t)\leq\left\lfloor\frac{n-t}{2}\right\rfloor\) ,真正統計時可分類討論 \(\max\) 中那個大得解。
\(\text{Part 2}\) : 實際實現
實際中上述 “換根” 並沒法很好維護,需重新分類討論。
以下將要按照最大子樹是否在 \(x\) 的子樹內,以及割的子樹是在 \(x\) 子樹內,或是 \(x\) 到根的路徑上,或除去 \(x\) 的子樹以及 \(x\) 到根的路徑上的位置進行討論:
\(1\):重兒子在 \(x\) 子樹內:
\(\text{ }\) \(1.1\):割的子樹不在 \(x\) 的重兒子的子樹內:
\(\text{ }\) \(\text{ }\) 此時分後 \(x\) 的最大兒子子樹大小是 \(mx\)
\(\text{ }\) \(\text{ }\) \(1.1.1\): 割的子樹在 \(x\) 的子樹內:
\(\text{ }\) \(\text{ }\) \(\text{ }\) \(x\) 所在的分後的樹大小為 \(n-t\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 則 \(mx \leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(1.1.2\): 割的子樹在 \(x\) 到根的路徑上:
\(\text{ }\) \(\text{ }\) \(\text{ }\) 注意此時 \(x\) 所在的分後的樹大小變成 \(t\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 所以 \(mx\leq \left\lfloor\frac{t}{2}\right\rfloor\) ,即 \(t\geq 2mx\)
\(\text{ }\) \(\text{ }\) \(1.1.3\): 割的子樹在非 \(x\) 子樹內且不在 \(x\) 到根路徑上的其他位置:
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(x\) 所在的分後的樹大小是 \(n-t\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 與 \(1.1.1\) 一樣 \(t\leq n-2mx\)
\(\text{ }\) \(1.2\): 割的子樹在 \(x\) 的重兒子內:
\(\text{ }\) \(\text{ }\) 此時分後 \(x\) 最大兒子子樹大小為 \(\max(mx1,mx-t)\)
\(\text{ }\) \(\text{ }\) \(x\) 所在分後的樹大小為 \(n-t\)
\(\text{ }\) \(\text{ }\) \(1.2.1\) \(mx-t\leq mx1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(t\geq mx-mx1\) 且 \(mx1 \leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 解得 \(mx-mx1\leq t\leq n-2mx1\)
\(\text{ }\) \(\text{ }\) \(1.2.2\) \(mx-t>mx1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(t\leq mx-mx1-1\) 且 \(mx-t\leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 解得 \(2mx-n\leq t\leq mx-mx1-1\)
\(2.\) 重兒子在 \(x\) 上方
\(\text{ }\) 此時 \(mx=n-size(x)\) , \(size(x)\) 為 \(x\) 的子樹大小
\(\text{ }\) \(2.1\) 不在重兒子內割
\(\text{ }\) \(\text{ }\) 此時相當於只在 \(x\) 的子樹內割
\(\text{ }\) \(\text{ }\) 割後 \(x\) 所在的樹大小為 \(n-t\) ,最大兒子子樹大小為 \(mx\)
\(\text{ }\) \(\text{ }\) 所以 \(mx\leq \left\lfloor\frac{n-t}{2}\right\rfloor\) ,即 \(t\leq n-2mx=2size(x)-n\)
\(\text{ }\) \(2.2\) 不在 \(x\) 子樹內且不在 \(x\) 到根的路徑上割
\(\text{ }\) \(\text{ }\) 此時割後 \(x\) 所在樹的大小為 \(n-t\) ,
\(\text{ }\) \(\text{ }\) 最大兒子子樹大小為 \(\max(mx-t,mx1)\leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(2.2.1\) \(mx-t\leq mx1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(mx-mx1\leq t\) 且 \(mx1\leq \left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 解得 \(n-size(x)-mx1\leq t\leq n-2mx1\)
\(\text{ }\) \(\text{ }\) \(2.2.2\) \(mx-t>mx1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(t\leq mx-mx1-1\) 且 \(mx-t\leq\left\lfloor\frac{n-t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 解得 \(2mx-n\leq t\leq mx-mx1-1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 也即 \(n-2size(x)\leq t\leq n-size(x)-mx1-1\)
\(\text{ }\) \(2.3\) 在 \(x\) 到根的路徑上割
\(\text{ }\) \(\text{ }\) 此時割後 \(x\) 所在的子樹大小為 \(t\)
\(\text{ }\) \(\text{ }\) 最大兒子子樹大小為 \(\max(t-size(x),mx1)\leq \left\lfloor\frac{t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(2.3.1\) \(t-size(x)\leq mx1\)
\(\text{ }\) \(\text{ }\) 此時 \(t\leq mx1+size(x)\) 且 \(mx1\leq \left\lfloor\frac{t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) 解得 \(2mx1\leq t\leq mx1+size(x)\)
\(\text{ }\) \(\text{ }\) \(2.3.2\) \(t-size(x)>mx1\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 此時 \(size(x)+mx1+1\leq t\) 且 \(t-size(x)\leq \left\lfloor\frac{t}{2}\right\rfloor\)
\(\text{ }\) \(\text{ }\) \(\text{ }\) 解得 \(size(x)+mx1+1\leq t\leq 2size(x)\)
一長坨的分類討論到此結束,考慮如何統計每種情況:
對於查 \(x\) 的子樹的資訊,可以以子樹大小為權值, \(\text{dfs}\) 序為根的編號建一顆主席樹。
對於查 \(x\) 到根的資訊,可以以子樹大小為權值,每次從其父親更新過來,建成另一棵主席樹。
注意 \(x\) 的子樹大小不必加進來,到其兒子的版本再加。
這樣不在 \(x\) 子樹內且不在 \(x\) 到根路徑上的資訊,可轉化為不在 \(x\) 子樹內的資訊減去 \(x\) 到根的資訊,這兩顆主席樹足以勝任。
時空複雜度都是 \(O(n\log n)\),由於分類討論眾多,常數略大。
程式碼:
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10,M=1e7+10;
int T,n,m,x,y,tot,edg,tt,lim,_lim,tmp,res;long long ans;
int to[N<<1],nextn[N<<1],h[N];char ch;
struct tree{int l,r,s;}t[M<<1];
int dfn[N],rev[N],size[N],son[N],mxsz[N],rt[N],rt1[N];
#define add(x,y) to[++edg]=y,nextn[edg]=h[x],h[x]=edg
inline void read(int &x){
x=0;ch=getchar();
while(ch<48||ch>57)ch=getchar();
while(ch>47&&ch<58)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(long long x){if(x>9)write(x/10);putchar(48+x%10);}
void update(int &k,int kk,int l,int r,int pos){
k=++tot;t[k]=t[kk];++t[k].s;
if(l^r){
int mid=(l+r)>>1;
if(pos<=mid)update(t[k].l,t[kk].l,l,mid,pos);
else update(t[k].r,t[kk].r,mid+1,r,pos);
}
}
void inquiry(int k1,int k2,int l,int r,int x,int y){
if(x<=l&&r<=y)res+=t[k2].s-t[k1].s;
else {
int mid=(l+r)>>1;
if(x<=mid)inquiry(t[k1].l,t[k2].l,l,mid,x,y);
if(mid<y)inquiry(t[k1].r,t[k2].r,mid+1,r,x,y);
}
}
void inquiry1(int k,int l,int r,int x,int y){
if(x<=l&&r<=y)res+=t[k].s;
else {
int mid=(l+r)>>1;
if(x<=mid)inquiry1(t[k].l,l,mid,x,y);
if(mid<y)inquiry1(t[k].r,mid+1,r,x,y);
}
}
void dfs(int x,int anc){
int i,y;rev[dfn[x]=++tt]=x;size[x]=1;
for(i=h[x];y=to[i],i;i=nextn[i])if(y^anc){
dfs(y,x);
size[x]+=size[y];
if(size[y]>=size[son[x]])mxsz[x]=size[son[x]],son[x]=y;
else if(size[y]>=mxsz[x])mxsz[x]=size[y];
}
if(n-size[x]>=size[son[x]])mxsz[x]=size[son[x]],son[x]=-1;
else if(n-size[x]>=mxsz[x])mxsz[x]=n-size[x];
}
void dfs_(int x,int anc){
if(anc>1)update(rt1[x],rt1[anc],1,n,size[anc]);int i,y;
for(i=h[x];y=to[i],i;i=nextn[i])if(y^anc)dfs_(y,x);
}
void inq(int x){
res=0;inquiry(rt[0],rt[dfn[x]-1],1,n,lim,_lim);tmp+=res;
res=0;inquiry(rt[dfn[x]+size[x]-1],rt[n],1,n,lim,_lim);tmp+=res;
res=0;inquiry1(rt1[x],1,n,lim,_lim);tmp-=res;
}
main(){
read(T);register int i;
while(T--){
read(n);
for(i=1;i^n;++i)read(x),read(y),add(x,y),add(y,x);
dfs(1,0);
for(i=2;i<=n;++i)update(rt[i],rt[i-1],1,n,size[rev[i]]);
dfs_(1,0);
for(x=1;x<=n;++x){
tmp=res=0;
if(son[x]<0){
lim=(size[x]<<1)-n;
if(lim>0)inquiry(rt[dfn[x]],rt[dfn[x]+size[x]-1],1,n,1,lim),tmp=res;
lim=max(n-size[x]-mxsz[x],1);_lim=n-(mxsz[x]<<1);
if(lim<=_lim)inq(x);
lim=max(n-(size[x]<<1),1);_lim=n-size[x]-mxsz[x]-1;
if(lim<=_lim)inq(x);
lim=(mxsz[x]<<1);_lim=mxsz[x]+size[x];res=0;
if(lim<=_lim)inquiry1(rt1[x],1,n,lim,_lim),tmp+=res;
lim=mxsz[x]+size[x]+1;_lim=(size[x]<<1);res=0;
if(lim<=_lim)inquiry1(rt1[x],1,n,lim,_lim),tmp+=res;
if(mxsz[x]<=(size[x]>>1))++tmp;
}
else {
y=son[x];lim=n-(size[y]<<1);
if(lim>0){
res=0;inquiry(rt[dfn[x]],rt[dfn[y]-1],1,n,1,lim);tmp=res;
res=0;inquiry(rt[dfn[y]+size[y]-1],rt[dfn[x]+size[x]-1],1,n,1,lim);tmp+=res;
res=0;inquiry(rt[0],rt[dfn[x]-1],1,n,1,lim);tmp+=res;
res=0;inquiry(rt[dfn[x]+size[x]-1],rt[n],1,n,1,lim);tmp+=res;
res=0;inquiry1(rt1[x],1,n,1,lim);tmp-=res;
}
res=0;lim=min(size[y]<<1,n);
inquiry1(rt1[x],1,n,lim,n);tmp+=res;
if(size[y]<=(size[x]>>1)&&x^1)++tmp;
lim=max(size[y]-mxsz[x],1);_lim=n-(mxsz[x]<<1);res=0;
if(lim<=_lim)inquiry(rt[dfn[y]-1],rt[dfn[y]+size[y]-1],1,n,lim,_lim),tmp+=res;
lim=max((size[y]<<1)-n,1);_lim=size[y]-mxsz[x]-1;res=0;
if(lim<=_lim)inquiry(rt[dfn[y]-1],rt[dfn[y]+size[y]-1],1,n,lim,_lim),tmp+=res;
}
ans+=1ll*tmp*x;
}
write(ans);putchar('\n');
for(i=1;i<=n;++i)h[i]=size[i]=mxsz[i]=son[i]=rt[i]=rt1[i]=0;
for(i=1;i<=tot;++i)t[i].l=t[i].r=t[i].s=0;
tot=edg=ans=tt=0;
}
}