zoukankan      html  css  js  c++  java
  • luoguP3233 [HNOI2014]世界树

    题意

    看见数据范围就知道是虚树,于是先建出虚树。

    考虑先求出虚树上的点的管理点,显然两边dfs,一遍从下往上,一遍从上往下。

    之后考虑不在虚树上的点,对于虚树上的每一条边((u,v)),我们考虑上面未出现在虚树上的点的个数是多少,显然是(u)包含(v)的儿子(x)的子树的大小减去(v)子树的大小,这个可以倍增求出。

    考虑(u,v)两点的管理点是否相等,如果相等那么直接加上(size[x]-size[v])即可,不相等(u->v)就必定存在一个分界点,于是我们可以倍增求出。

    这时还会有考虑不到的点,用个数组(g[x])记下x的子树中有多少点未被分配,之后并到(x)上。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=300010;
    int n,m,cnt,tim,top,t,num;
    int head[maxn],a[maxn],sta[maxn],dfn[maxn],dep[maxn],ans[maxn],size[maxn],b[maxn],c[maxn],col[maxn],g[maxn];
    int f[maxn][20];
    struct edge{int to,nxt;}e[maxn<<1];
    inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
    inline void add(int u,int v)
    {
    	e[++cnt].nxt=head[u];
    	head[u]=cnt;
    	e[cnt].to=v;
    }
    void dfs_pre(int x,int fa)
    {
    	dep[x]=dep[fa]+1;size[x]=1;dfn[x]=++tim;
    	for(int i=1;i<=t;i++)f[x][i]=f[f[x][i-1]][i-1];
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int y=e[i].to;
    		if(y==fa)continue;
    		f[y][0]=x;dfs_pre(y,x);size[x]+=size[y];
    	}
    }
    inline int lca(int x,int y)
    {
    	if(dep[x]>dep[y])swap(x,y);
    	for(int i=t;~i;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];
    	if(x==y)return x;
    	for(int i=t;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    inline int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
    void dfs1(int x)
    {
    	c[++num]=x;g[x]=size[x];
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int y=e[i].to;
    		dfs1(y);
    		if(!col[y])continue;
    		if(!col[x]){col[x]=col[y];continue;}
    		int d1=dis(col[y],x),d2=dis(col[x],x);
    		if(d1<d2||(d1==d2&&col[y]<col[x]))col[x]=col[y];
    	}
    }
    void dfs2(int x)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		int y=e[i].to;
    		if(!col[y])col[y]=col[x];
    		else 
    		{
    			int d1=dis(col[y],y),d2=dis(col[x],y);
    			if(d1>d2||(d1==d2&&col[x]<col[y]))col[y]=col[x];
    		}		
    		dfs2(y);
    	}	
    }
    inline void work(int x,int y)
    {
    	int now=y;
    	for(int i=t;~i;i--)if(dep[now]-(1<<i)>dep[x])now=f[now][i];
    	g[x]-=size[now];
    	if(col[x]==col[y]){ans[col[x]]+=size[now]-size[y];return;}
    	int tmp=y;
    	for(int i=t;~i;i--)
    	{
    		int nxt=f[tmp][i];
    		if(dep[nxt]<=dep[x])continue;
    		int d1=dis(nxt,col[x]),d2=dis(nxt,col[y]);
    		if(d1>d2||(d1==d2&&col[y]<col[x]))tmp=nxt;
    	}
    	ans[col[x]]+=size[now]-size[tmp],ans[col[y]]+=size[tmp]-size[y];
    }
    inline void solve()
    {
    	num=cnt=0;
    	int tot;scanf("%d",&tot);
    	for(int i=1;i<=tot;i++)scanf("%d",&a[i]),b[i]=a[i],col[a[i]]=a[i];
    	sta[top=1]=1;head[1]=0;
    	sort(a+1,a+tot+1,cmp);
    	for(int i=1;i<=tot;i++)
    	{
    		if(a[i]==1)continue;
    		int x=lca(sta[top],a[i]);
    		if(x!=sta[top])
    		{
    			while(top>1&&dfn[sta[top-1]]>dfn[x])add(sta[top-1],sta[top]),top--;
    			if(x!=sta[top-1])head[x]=0,add(x,sta[top]),sta[top]=x; 
    			else add(x,sta[top--]);
    		}
    		head[a[i]]=0,sta[++top]=a[i];
    	}
    	for(int i=1;i<top;i++)add(sta[i],sta[i+1]);
    	dfs1(1),dfs2(1);
    	for(int j=1;j<=num;j++)
    		for(int i=head[c[j]];i;i=e[i].nxt)
    			work(c[j],e[i].to);
    	for(int i=1;i<=num;i++)ans[col[c[i]]]+=g[c[i]];
    	for(int i=1;i<=tot;i++)printf("%d ",ans[b[i]]);
    	puts("");
    	for(int i=1;i<=num;i++)ans[c[i]]=g[c[i]]=col[c[i]]=0;
    }
    int main()
    {
    	scanf("%d",&n);t=(int)log2(n)+1;
    	for(int i=1;i<n;i++)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		add(u,v),add(v,u);
    	}	
    	dfs_pre(1,0);
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++)solve();
    	return 0;
    }
    
  • 相关阅读:
    WeihanLi.Npoi 1.10.0 更新日志
    消除代码中的坏味道,编写高质量代码
    代码重构之法——方法重构分析
    使用 C# 捕获进程输出
    .net core 中的经典设计模式的应用
    JDBC 规范中文版 4.2 -第一章 简介
    基础回顾-线程的几种状态
    一文读懂BeanFactory和FactoryBean区别
    阿里云云计算ACA 第三章 阿里云存储服务
    阿里云云计算ACA 第二章 阿里云弹性计算
  • 原文地址:https://www.cnblogs.com/nofind/p/11976096.html
Copyright © 2011-2022 走看看