zoukankan      html  css  js  c++  java
  • BZOJ3572 [Hnoi2014]世界树

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    Description

    世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
    世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
    出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
    现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

    Input

    第一行为一个正整数n,表示世界树中种族的个数。
    接下来n-l行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
    向道路。接下来一行为一个正整数q,表示国王询问的年数。
    接下来q块,每块两行:
    第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
    第i块的第二行为m[i]个正整数h[l]、h[2]、…、h[m[i]],表示被授权为临时议事处的聚居地编号(保证互不相同)。

    Output

    输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2…,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。

    Sample Input

    10
    2 1
    3 2
    4 3
    5 4
    6 1
    7 3
    8 3
    9 4
    10 1
    5
    2
    6 1
    5
    2 7 3 6 9
    1
    8
    4
    8 7 10 3
    5
    2 9 3 5 8

    Sample Output

    1 9
    3 1 4 1 1
    10
    1 1 3 5
    4 1 3 1 1

    HINT

     

    N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000

    正解:虚树+DP+倍增

    解题报告:

      参考了hzwer的博客

      同虚树的题目一样,这道题显然是先要建出虚树,然后复杂度就只和关键点有关了。

      对于每次询问建一棵虚树,建法参见BZOJ2286

      之后我们在虚树上dfs一遍,得到每个点从属于哪个节点。之后我们只要统计不在虚树中的点(在虚树中的点上面统计了下面不作考虑)。

      考虑虚树上的某一条边,如果两个点同属于一个节点,那么只要加上两点之间的未在虚树中的点数即可。

      假如两个点不属于同一节点,那么显然中间会存在分界点,倍增地找出这个分界点mid,然后两边分别计算贡献就可以了。

      注意我还需要记一个g数组,表示未在上述统计到的节点数,因为有一些点没有在任何一次讨论中被考虑,那么显然将会与他们在虚树上的父亲节点属于同一节点,只要把初值设为size,每次在上面讨论一次,就把讨论的部分删掉即可。

    //It is made by ljh2000
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN = 300011;
    const int MAXM = 600011;
    int n,ecnt,next[MAXM],to[MAXM],first[MAXN],dfn[MAXN],size[MAXN],deep[MAXN],f[MAXN][19];
    int tot,a[MAXN],b[MAXN],belong[MAXN],stack[MAXN],top,g[MAXN],p[MAXN],cnt,c[MAXN];
    inline void link(int x,int y){ if(x==0 || x==y) return ; next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    inline bool cmp(int x,int y){ return dfn[x]<dfn[y]; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void dfs(int x,int fa){
    	size[x]=1; dfn[x]=++ecnt;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		f[v][0]=x; deep[v]=deep[x]+1; 
    		dfs(v,x); size[x]+=size[v];
    	}
    }
    
    inline int lca(int x,int y){
    	if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
    	for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return x;
    	for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
    }
    
    inline int getdis(int x,int y){ return deep[x]+deep[y]-deep[lca(x,y)]*2; }
    
    inline void dfs1(int x,int fa){
    	int d1,d2; g[x]=size[x]; c[++cnt]=x;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		dfs1(v,x); if(belong[v]==0)  continue;
    		if(belong[x]==0) { belong[x]=belong[v]; continue; }//注意不要出0了
    		d1=getdis(belong[v],x); d2=getdis(belong[x],x);
    		if(d1<d2 || (d1==d2 && belong[v]<belong[x])) belong[x]=belong[v];
    	}
    }
    
    inline void dfs2(int x,int fa){
    	int d1,d2;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		if(belong[v]==0) { belong[v]=belong[x]; }//没有归属的情况,直接划归治下!
    		else {
    			d1=getdis(belong[v],v); d2=getdis(belong[x],v);
    			if(d1>d2 || (d1==d2 && belong[v]>belong[x])) belong[v]=belong[x];
    		}
    		//必须先划分再下传!不然会有问题!
    		dfs2(v,x);	
    	}
    }
    
    inline void solve(int fa,int u){//讨论(fa,u)这条边
    	int son=u,mid=u,nex,d1,d2;
    	for(int i=18;i>=0;i--)//son是fa到u的路径上的第一个节点,也就是一个原树上的儿子节点 
    		if(deep[son]-(1<<i)>deep[fa])
    			son=f[son][i];
    
    	g[fa]-=size[son];//减去加入讨论的部分,保留仍未讨论的部分,是size而不是g!
    	if(belong[fa]==belong[u]) { p[belong[fa]]+=size[son]-size[u]; return ; }
    	for(int i=18;i>=0;i--) {//寻找分界点
    		nex=f[mid][i]; if(deep[nex]<=deep[fa]) continue;
    		d1=getdis(nex,belong[fa]); d2=getdis(nex,belong[u]);
    		if(d1>d2 || (d1==d2 && belong[u]<belong[fa])) mid=nex;
    	}
    	p[belong[fa]]+=size[son]-size[mid];
    	p[belong[u]]+=size[mid]-size[u];
    }
    
    inline void build(){
    	tot=getint(); for(int i=1;i<=tot;i++) a[i]=getint(),b[i]=a[i];
    	for(int i=1;i<=tot;i++) belong[a[i]]=a[i];
    	top=ecnt=cnt=0; sort(a+1,a+tot+1,cmp); if(belong[1]!=1) stack[++top]=1;
    	int LCA;
    	for(int i=1;i<=tot;i++) {
    		if(top==0) { stack[++top]=a[i]; continue; }
    		LCA=lca(stack[top],a[i]);
    		while(1) {
    			if(deep[stack[top-1]]<=deep[LCA]) {
    				link(LCA,stack[top]); top--;
    				if(stack[top]!=LCA) stack[++top]=LCA;
    				break;
    			}
    			link(stack[top-1],stack[top]);
    			top--;
    		}
    		if(stack[top]!=a[i]) stack[++top]=a[i];
    	}
    	while(top>1) link(stack[top-1],stack[top]),top--; top--; //记得把剩下的连完边!
    
    	dfs1(1,0); 
    	dfs2(1,0);
    
    	//对于虚树上的每一条边进行考虑
    	for(int i=1;i<=cnt;i++) 
    		for(int j=first[c[i]];j;j=next[j])
    			solve(c[i],to[j]);
    
    	for(int i=1;i<=cnt;i++) p[belong[c[i]]]+=g[c[i]];
    	for(int i=1;i<=tot;i++) printf("%d ",p[b[i]]); puts("");
    	for(int i=1;i<=cnt;i++) p[c[i]]=first[c[i]]=g[c[i]]=belong[c[i]]=0;//需要清空的是所有!不只是tot
    }
    
    inline void work(){
    	n=getint(); int x,y;
    	for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
    	ecnt=0;	dfs(1,0); ecnt=0; memset(first,0,sizeof(first));
    	for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    	int q=getint();
    	while(q--) build();
    }
    
    int main()
    {
        work();
        return 0;
    }
    

      

  • 相关阅读:
    20145237 《信息安全系统设计基础》第八周学习总结
    实验二 20145237 20155226 2015234 实验报告 固件程序设计
    实验一(开发环境的熟悉)问题总结
    实验二(固件设计)问题总结
    实验五(简单嵌入式WEB服务器实验)问题总结
    实验三( 实时系统的移植)问题总结
    实验四(外设驱动程序设计)问题总结
    20145235《信息安全系统设计基础》课程总结
    教材配套项目——缓冲区实验
    20145235 《信息安全系统设计基础》第十四周学习总结
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6432759.html
Copyright © 2011-2022 走看看