世界树(HNOI2014)

世界树(HNOI2014)

题目描述

给出一棵有\(n\)个点,边权全部为\(1\)的树,有\(q\)个询问。

对于每个询问,都会给出树中的\(m\)个点,对于所有的\(n\)个点,每个点都会附属于这\(m\)个点中距离它本身最近的点(若满足条件,可以附属于自身上),要求你求出对于这\(m​\)个点分别有多少个节点附属于它。

\(\sum{m}<=300000\)

思路

这题其实(据说)用虚树写蛮简单,但由于本人较弱不会,讲一个\(dfs序+线段树+倍增LCA+离线\)的方法。。。

首先是预处理倍增,LCA,\(dfs\)序等,这些不多说。接下来我们做这样的操作:把每次询问的\(m\)个点按照点的深度排序。如果按此序依次放入每一个点,就不可能在该点的子树中存在之前放入的点。至于这个小性质有啥用,我们待会在说。。

我们考虑放入一个点后,对那些点的附属值产生影响。这时排序的作用就来了:在这点变成可附属点时,它原本就会有一个它的附属点,那么这样排序就只会影响该点的附属值,说简单点就是:当\(u\)原本附属于\(v\)时,当\(u\)变为可附属点时,只会影响\(v\)的附属值。如何证明应该不用赘述吧!应该画个图就可以理解了,注意是按深度排序的就行了!

接下来就是\(u\)会抢走\(v\)的几个点呢?同理,通过画图以及人类智慧可知,正是从\(u\)\(v\)的路径的中心点\(w\)的子树,由于\(u\)深度大于\(v\)深度,所以\(w\)肯定是\(u\)的祖先,所以就要用倍增跳\(u\),跳到\(w\),然而注意,如果\(w\)\(u\),\(v\)的路径相等时,还要特判两者的编号大小并以此决定向上跳的路径。路径长度便需要\(LCA\)求解。

最后就是如何维护每个点的附属点,这个就不复杂了,由于每次抢走的就是一个子树,所以只要\(dfs\)序区间更新在线段树上即可,若是第一个点(即一开始没可附属点时),就把所有的点(即1~n区间)更新了。

这样综合了之后,这题便得到圆满解决,还有什么细节详见代码。

代码

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l,i##R=r;i<=i##R;i++)
#define DOR(i,r,l) for(int i=r,i##L=l;i>=i##L;i--)
#define loop(i,n) for(int i=0,i##R=n;i<i##R;i++)
#define sf scanf
#define pf printf
#define mms(a,x) memset(a,x,sizeof a)
using namespace std;
typedef long long ll;
typedef long double db;
template<typename A,typename B>inline void chkmax(A &x,const B y){if(x<y)x=y;}
template<typename A,typename B>inline void chkmin(A &x,const B y){if(x>y)x=y;}
const int N=3e5+5;
int n,m,q;
struct Graph{//正向表
	int tot,to[N<<1],nxt[N<<1],head[N];
	void add(int x,int y){tot++;to[tot]=y;nxt[tot]=head[x];head[x]=tot;}
	void clear(){mms(head,-1);tot=0;}
	#define EOR(G,i,x) for(int i=G.head[x];i!=-1;i=G.nxt[i])
}G;
int cnt[N];
int ans[N];
struct node{
	int x,id;
}A[N];
int ct,sz[N],top[N],son[N],dep[N],fa[N][20];
int dfn[N],post[N];
bool cmp(node a,node b){//先按深度排,再按编号排,不能忽略编号
	if(dep[a.x]!=dep[b.x])return dep[a.x]<dep[b.x];
	return a.x<b.x;
}
struct Pt2{
	struct YD_Tree{//维护每个点的附属点
		#define ls (p<<1)
		#define rs (p<<1|1)
		static const int M=(N<<2);
		int bel[M];
		void down(int p){
			if(!bel[p])return;
			bel[ls]=bel[rs]=bel[p];
			bel[p]=0;
		}
		void update(int p,int l,int r,int L,int R,int x){
			if(L<=l&&r<=R){
				bel[p]=x;
				return;
			}
			down(p);
			int mid=(l+r)>>1;
			if(mid<R)update(rs,mid+1,r,L,R,x);
			if(mid>=L)update(ls,l,mid,L,R,x);
		}
		int query(int p,int l,int r,int pos){
			if(bel[p])return bel[p];
			int mid=(l+r)>>1;
			if(mid<pos)return query(rs,mid+1,r,pos);
			else return query(ls,l,mid,pos);
		}
	}Tr;
	void dfs(int x,int f){
		fa[x][0]=f;
		dfn[x]=++ct;
		dep[x]=dep[f]+1;
		sz[x]=1,son[x]=0;
		EOR(G,i,x){
			int v=G.to[i];
			if(v==f)continue;
			dfs(v,x);
			sz[x]+=sz[v];
			if(sz[son[x]]<sz[v])son[x]=v;
		}
		post[x]=ct;
	}
	void top_dfs(int x,int f,int tp){
		top[x]=tp;
		if(son[x])top_dfs(son[x],x,tp);
		EOR(G,i,x){
			int v=G.to[i];
			if(v==f||v==son[x])continue;
			top_dfs(v,x,v);
		}
	}
	int LCA(int a,int b){//个人用了跳重链,常数较小,嫌烦的直接倍增完全没问题
		while(top[a]!=top[b]){
			if(dep[top[a]]>dep[top[b]])a=fa[top[a]][0];
			else b=fa[top[b]][0];
		}
		return dep[a]>dep[b]?b:a;
	}
	void jump(int &x,int len){//倍增跳点
		FOR(i,0,19)
			if((len>>i)&1)x=fa[x][i];
	}
	void solve(){
		sf("%d",&q);
		dfs(1,0);
		top_dfs(1,0,1);
		FOR(j,1,19)FOR(i,1,n)
			fa[i][j]=fa[fa[i][j-1]][j-1];
		while(q--){
			sf("%d",&m);
			FOR(i,1,m){
				sf("%d",&A[i].x);
				A[i].id=i;
				ans[i]=0,cnt[A[i].x]=0;
			}
			sort(A+1,A+m+1,cmp);
			Tr.update(1,1,n,1,n,A[1].x);//第一个点放入时,所有点都附属于它
			cnt[A[1].x]=n;
			FOR(i,2,m){
				int x=A[i].x;
				int last=Tr.query(1,1,n,dfn[x]);//原附属点
				int lca=LCA(x,last);
				int dis=dep[last]+dep[x]-(dep[lca]<<1);
				jump(x,(dis-(x>last))>>1);
				// pf("after jumped:%d sz:%d\n",x,sz[x]);
				cnt[A[i].x]+=sz[x];
				cnt[last]-=sz[x];
				Tr.update(1,1,n,dfn[x],post[x],A[i].x);
			}
			FOR(i,1,m)ans[A[i].id]=cnt[A[i].x];
			FOR(i,1,m)pf("%d ",ans[i]);puts("");
		}
	}
}Pt_2;
int main(){
	freopen("worldtree.in","r",stdin);
	freopen("worldtree.out","w",stdout);
	G.clear();
	sf("%d",&n);
	FOR(i,1,n-1){
		int x,y;
		sf("%d%d",&x,&y);
		G.add(x,y),G.add(y,x);
	}
	Pt_2.solve();
	return 0;
}

posted @ 2019-04-18 21:10  Hëinz  阅读(279)  评论(0编辑  收藏  举报