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

    【BZOJ3572】[Hnoi2014]世界树

    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

    题解:先建立虚树,然后处理出对于虚树上的每个节点,离它最近的议事处的编号xi及距离di,然后考虑不再虚树上的点:

    如果某个连通块中的点只有一种途径到达虚树上的点,那么他们显然只能归那个点的xi管辖,直接统计一下就好。

    如果某个连通块中的点有两种途径到达虚树上的点,那么他们要被这两个点分掉。我们根据di算出分割点,然后倍增找到这个点,那么在上侧的点会被上面的点管辖,下侧的点被下侧的管辖,统计一下siz就好。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int maxn=300010;
    int n,m,tot,top,rt,cnt;
    int to[maxn<<1],next[maxn<<1],head[maxn],dep[maxn],fa[20][maxn],p[maxn],q[maxn],Log[maxn],siz[maxn],vis[maxn];
    int st[maxn],f[maxn],pos[maxn];
    vector<int> ch[maxn];
    struct node
    {
    	int v,x;
    	node() {}
    	node(int a,int b) {v=a,x=b;}
    	node operator + (const int &a) const {return node(v+a,x);}
    	bool operator < (const node &a) const	{return (v==a.v)?(x<a.x):v<a.v;}
    }s[maxn];
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    bool cmp(int a,int b)
    {
    	return (pos[a]>pos[b]);
    }
    void dfs(int x)
    {
    	pos[x]=++pos[0],siz[x]=1;
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])
    		fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]),siz[x]+=siz[to[i]];
    }
    inline int lca(int a,int b)
    {
    	if(dep[a]<dep[b])	swap(a,b);
    	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	a=fa[i][a];
    	if(a==b)	return a;
    	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	a=fa[i][a],b=fa[i][b];
    	return fa[0][a];
    }
    inline int FA(int x,int y)
    {
    	for(int i=Log[y];i>=0;i--)	if((1<<i)<=y)	y-=(1<<i),x=fa[i][x];
    	return x;
    }
    void dfs1(int x)
    {
    	int i,y;
    	if(vis[x])	s[x]=node(0,x);
    	else	s[x]=node(1<<20,0);
    	for(i=0;i<(int)ch[x].size();i++)	y=ch[x][i],dfs1(y),s[x]=min(s[x],s[y]+(dep[y]-dep[x]));
    }
    void dfs2(int x)
    {
    	int i,y,z,k;
    	f[s[x].x]+=siz[x];
    	for(i=0;i<(int)ch[x].size();i++)
    	{
    		y=ch[x][i],s[y]=min(s[y],s[x]+(dep[y]-dep[x])),dfs2(y);
    		z=FA(y,dep[y]-dep[x]-1),f[s[x].x]-=siz[z];
    		if(s[x].x==s[y].x)	f[s[x].x]+=siz[z]-siz[y];
    		else
    		{
    			if(s[x].x<s[y].x)	k=FA(y,(s[x].v-s[y].v+dep[y]-dep[x]-1)/2);
    			else	k=FA(y,(s[x].v-s[y].v+dep[y]-dep[x])/2);
    			f[s[x].x]+=siz[z]-siz[k],f[s[y].x]+=siz[k]-siz[y];
    		}
    	}
    	ch[x].clear();
    }
    int main()
    {
    	n=rd();
    	int i,j,a,b;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	dep[1]=1,dfs(1);
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]];
    	m=rd();
    	for(i=1;i<=m;i++)
    	{
    		tot=rd();
    		for(j=1;j<=tot;j++)	q[j]=p[j]=rd(),vis[p[j]]=1;
    		sort(p+1,p+tot+1,cmp);
    		st[top=1]=p[1];
    		for(j=2;j<=tot;j++)
    		{
    			a=lca(p[j-1],p[j]);
    			while(top&&dep[st[top]]>dep[a])
    			{
    				b=st[top],top--;
    				if(top&&dep[st[top]]>dep[a])	ch[st[top]].push_back(b);
    				else	ch[a].push_back(b);
    			}
    			if(!top||st[top]!=a)	st[++top]=a;
    			if(st[top]!=p[j])	st[++top]=p[j];
    		}
    		while(top>1)	ch[st[top-1]].push_back(st[top]),top--;
    		rt=st[top];
    		dfs1(rt);
    		dfs2(rt);
    		f[s[rt].x]+=n-siz[rt];
    		for(j=1;j<=tot;j++)	printf("%d ",f[q[j]]),f[q[j]]=vis[q[j]]=0;
    		puts("");
    	}
    	return 0;
    }//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 
  • 相关阅读:
    FullCalendar日历插件说明文档
    Git忽略规则及.gitignore规则不生效的解决办法
    学习git config配置文件
    拼接最长回文串
    Gildong owns a bulgogi restaurant
    前m大的数
    求水洼(dfs)
    循环数组的最大子段和
    求叶子节点
    周期
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7787532.html
Copyright © 2011-2022 走看看