zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    14、数列
    13、Hangover
    12、Anagrams by Stack
    彩票软件7) 重构数据库accesser
    彩票软件6)观察者模式
    彩票软件5)Sqlite 数据库访问类
    彩票软件4)插叙
    彩票软件3)wpf界面布局
    彩票软件2)代码管理git
    彩票软件1)前言
  • 原文地址:https://www.cnblogs.com/zzqtxdy/p/12185072.html
Copyright © 2011-2022 走看看