LOJ#6042「雅礼集训 2017 Day7」跳蚤王国的宰相

题目大意

一棵树,每次操作可以\(cut\)\(link\)一次,对每个点求最少多少次操作后这个点变为重心。

题解

为了方便分析,找一个重心拉出来作为根。
考虑一个点,不难发现删掉的子树只可能是根或根的其他儿子,否则往上走不会变劣。
然后就可以随便维护了。
个人做法:
把根所有儿子的\(siz\)拉出来,二分一下,\(check\)的时候就是求挖掉一个数后前\(k\)大的和,预处理前缀和即可,详见代码。
切掉根的代价就是当前根儿子的\(siz\)减当前点的\(siz\),算答案的时候前\(mid\)大的和和前\(mid-1\)大的和+切掉根的代价取\(min\)即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
int rd(){
    int x=0,flg=1;
    char c=getchar();
    for (;(c<48||c>57)&&c!='-';c=getchar());
    if (c=='-') flg=-1,c=getchar();
    for (;c>47&&c<58;x=x*10+c-48,c=getchar());
    return flg*x;
}
const int mxn=1000010;
int n,m,rt,num,head[mxn],siz[mxn],a[mxn],s[mxn],pos,ans[mxn];
struct ed{int to,nxt;}edge[mxn<<1];
void addedge(int u,int v){
    edge[++m]=(ed){v,head[u]},head[u]=m;
    edge[++m]=(ed){u,head[v]},head[v]=m;
}
void getrt(int u,int fa){
    siz[u]=1;
    int mx=0;
    for (int i=head[u],v;i;i=edge[i].nxt)
        if ((v=edge[i].to)!=fa) getrt(v,u),siz[u]+=siz[v],mx=max(mx,siz[v]);
    mx=max(mx,n-siz[u]);
    if (mx<=num) num=mx,rt=u;
}
int f(int x,int nm){
    if (!x) return 0;
    int y=x+(x>=pos);
    if (a[y]<nm){
        --x,y=x+(x>=pos);
        return s[y]-(y>=pos)*num+nm;
    }
    return s[y]-(y>=pos)*num;
}
void dfs(int u,int fa){
    int l=0,r=m;
    for (;l<=r;)
        if (n-siz[u]-f(mid,num-siz[u])<=n>>1) r=mid-1;
        else l=mid+1;
    ans[u]=l;
    for (int i=head[u],v;i;i=edge[i].nxt)
        if ((v=edge[i].to)!=fa) dfs(v,u);
}
bool cmp(const int &x,const int &y){
    return x>y;
}
int main()
{
    n=rd();
    for (int i=1,x,y;i<n;++i)
        x=rd(),y=rd(),addedge(x,y);
    num=1e9,getrt(1,0);
    num=1e9,getrt(rt,0);
    m=0;
    for (int i=head[rt];i;i=edge[i].nxt) a[++m]=siz[edge[i].to];
    sort(a+1,a+m+1,cmp);
    for (int i=1;i<=m;++i) s[i]=s[i-1]+a[i];
    for (int i=head[rt];i;i=edge[i].nxt){
        int v=edge[i].to,l=1,r=m;
        for (;l<=r;)
            if (a[mid]>siz[v]) l=mid+1;
            else r=mid-1;
        pos=l;
        num=siz[v];
        dfs(v,rt);
    }
    for (int i=1;i<=n;++i)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzqtxdy/p/12185072.html
今日推荐