题目描述
跳蚤王国爆发了一场动乱,国王在镇压动乱的同时,需要在跳蚤国地方钦定一个人来做宰相。
然而当时国王的钦定方式很奇怪,跳蚤王国可以看成一棵树,国王认为宰相必须更好地位跳蚤服务,所以他会选择一个到所有节点距离和最小的节点,并在这个节点中钦定,如果有多个节点满足距离和最小则任选一个。
然而跳蚤国的动乱实在是太厉害了,以至于树的心态可能也会发生改变也就是说,树上可能会有若干条边消失,如果这个情况出现的话一定会有同样数目的边出现,以保证整个结构仍然是一棵树。
现在这个跳蚤想知道每个节点中的跳蚤如果要被钦定,至少需要多少条边消失(当然也有同样数目的边出现)。作为这只跳蚤的一名真正的粉丝,你能帮他解决这个问题吗?
贪心
显然是问删了再加至少多少边使一个点成为重心。
重心的定义是所有儿子子树大小不超过n/2。
切了若干刀后,显然
一个显然的暴力是枚举一个点算它的答案,那么就以它为根。
然后对于一个子树计算f[i][0~1],分别表示至少切多少下,以及切了这么多下后i所在联通块的大小。
那么如何转移了,先需要儿子分别被切到<=n/2的次数,然后按照剩余大小排序,从大往小切,直至它的剩余大小也<=n/2。
这是很显然的贪心吧!但是会T。
我们考虑钦点根后,还是能求出f,而对于一个点x,还需要求一个g,表示除它的子树外的部分要切多少下,可以由父亲转移而来,转移很简单,使用二分找到至少要切多少个子树即可。详见代码。
真是好水的题呢!
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000000+10;
int h[maxn],go[maxn*2],next[maxn*2],size[maxn],father[maxn],a[maxn];
int f[maxn][2],g[maxn][2],sum[maxn],wz[maxn],sta[80];
int i,j,k,l,r,mid,t,n,m,tot,top,ans;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
bool cmp(int x,int y){
return f[x][1]>f[y][1];
}
void dfs(int x,int y){
father[x]=y;
int t=h[x];
size[x]=1;
while (t){
if (go[t]!=y){
dfs(go[t],x);
size[x]+=size[go[t]];
}
t=next[t];
}
top=0;
int i;
t=h[x];
while (t){
if (go[t]!=y) a[++top]=go[t];
t=next[t];
}
fo(i,1,top) f[x][0]+=f[a[i]][0],f[x][1]+=f[a[i]][1];
f[x][1]++;
sort(a+1,a+top+1,cmp);
fo(i,1,top){
if (f[x][1]<=n/2) break;
f[x][0]++;
f[x][1]-=f[a[i]][1];
}
}
void dg(int x,int y){
int i,j,k=0,l,r,mid,t;
top=0;
t=h[x];
while (t){
if (go[t]!=y) a[++top]=go[t];
t=next[t];
}
a[++top]=n+1;
f[n+1][0]=g[x][0];
f[n+1][1]=g[x][1];
sort(a+1,a+top+1,cmp);
fo(i,1,top) wz[a[i]]=i;
fo(i,1,top) sum[i]=sum[i-1]+f[a[i]][1];
fo(i,1,top) k+=f[a[i]][0];
t=h[x];
while (t){
if (go[t]!=y){
l=0;r=top;
while (l<r){
mid=(l+r)/2;
j=sum[mid];
if (wz[go[t]]<=mid) j-=f[go[t]][1];
if (sum[top]-f[go[t]][1]+1-j<=n/2) r=mid;else l=mid+1;
}
g[go[t]][0]=k-f[go[t]][0]+l;
if (wz[go[t]]<=l) g[go[t]][0]--;
j=sum[l];
if (wz[go[t]]<=l) j-=f[go[t]][1];
g[go[t]][1]=sum[top]-f[go[t]][1]+1-j;
}
t=next[t];
}
t=h[x];
while (t){
if (go[t]!=y) dg(go[t],x);
t=next[t];
}
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top) putchar('0'+sta[top--]);
putchar('\n');
}
int main(){
freopen("flea.in","r",stdin);freopen("flea.out","w",stdout);
n=read();
fo(i,1,n-1){
j=read();k=read();
add(j,k);add(k,j);
}
dfs(1,0);
dg(1,0);
fo(i,1,n){
ans=0;
t=h[i];
while (t){
if (go[t]!=father[i]) ans+=f[go[t]][0];
t=next[t];
}
ans+=g[i][0];
write(ans);
}
}