HNOI2014D1T2 worldtree 题解

【问题描述】
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。世界树的形态可以用一个数学模型来描述:  世界树中有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年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。
现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
【输入格式】
输入文件名为worldtree.in。
第一行为一个正整数n,表示世界树中种族的个数。
接下来n-1行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。
接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i 块的第二行为m[i]个正整数h[1]、h[2]、…、h[m[i]],表示被授权为临时议事处
的聚居地编号(保证互不相同)。
【输出格式】 
输出文件名为worldtree.out。
输出包含q行,第i 行为m[i]个整数,该行的第j(j=1,2,…,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。


      这题考场上想到了树链剖分,但是不知道应该维护什么信息。回来看了Linux机房的某个大神的代码才明白怎么写。主要思想就是把议事处按照深度和编号大小排序,然后把树上的点按规则分配给第1~i个议事点,然后再去更新第i+1个议事处应该管辖哪些点(更新答案时,一个点u被点v管辖代表着点u及其子树都被点v管辖)。用树链剖分来维护每条重链上最深的被管辖的点属于哪个议事处。做到第i个议事处x时,找到所在的重链上最深的被管辖的点所属的议事处y(如果所在的重链上没有已经分配的点,就一直往root跳)。两个点之间的距离dis=dep[x]+dep[y]-2*dep[lca(x,y)]。因为x的深度必然大于等于y的深度,那么x到y这条路径上下半部分的点都属于x管辖,上半部分属于y管辖,对于到x和y距离相等的点,属于编号较小的y管辖,用线段树来维护即可。


CODE

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
class DREAD{
	friend int fread();
	static const int buff_max=20000000+10;
	char buf[buff_max],*pt;
	public:
		void clear(){ for(; isspace(*pt); ++pt); }
		void Init(){ buf[fread(buf,1,buff_max,stdin)]=EOF;pt=buf; }
		bool eoln(){ return *pt=='\n'; }
		inline bool eof(){ return *pt==EOF; }
		int Int(){
			bool pos=1;int res=0;
			for (; !isdigit(*pt) && *pt!='-' && !eof(); ++pt);if (*pt=='-') pos=0,++pt;
			for (; isdigit(*pt); ++pt) res=res*10+(*pt-'0');
			return pos?res:-res;
		}
}READ;
const int N_MAX=300000+10,M_MAX=600000+10;
int pos[N_MAX],pre[M_MAX],son[M_MAX],tot=0;
void add(int a,int b){
	pre[++tot]=pos[a]; pos[a]=tot; son[tot]=b;
	pre[++tot]=pos[b]; pos[b]=tot; son[tot]=a;
}
int N,Q;
namespace Nwork{
	int qs[N_MAX],ans[N_MAX],in[N_MAX];
	const int root=1;
	int heavy[N_MAX],fa[N_MAX],size[N_MAX],top[N_MAX],newid[N_MAX],dep[N_MAX],usedid=0;
	bool cmp(int x,int y){
		return dep[x]==dep[y]?x<y:dep[x]<dep[y];
	}
	/*深度做第一关键字*/
	int st[20][N_MAX];
	void dfs1(int x=root,int f=0){
		int maxsize=-1;
		size[x]=1; fa[x]=f;
		for (int i=1; i<=19; ++i)
			st[i][x]=st[i-1][st[i-1][x]];
		for (int p=pos[x]; p; p=pre[p])
			if (son[p]!=f){
				st[0][son[p]]=x; dep[son[p]]=dep[x]+1;
				dfs1(son[p],x);
				size[x]+=size[son[p]];
				if (size[son[p]]>maxsize) heavy[x]=son[p],maxsize=size[son[p]];
			}
	}
	void dfs2(int x=root,int tp=root){
		newid[x]=++usedid; top[x]=tp;
		if (heavy[x]) dfs2(heavy[x],tp);
		for (int p=pos[x]; p; p=pre[p])
			if (son[p]!=fa[x] && son[p]!=heavy[x])
				dfs2(son[p],son[p]);
	}
	/*树链剖分*/
	struct Tseg{
		static const int SEG_MAX=300000*8+10;
		struct Tnode{
			int val,tag;
			Tnode():val(0),tag(-1){}
		}t[SEG_MAX];
		#define ls(_) ((_)<<1)
		#define rs(_) (ls(_)|1)
		void change(int p,int x){
			t[p].val=t[p].tag=x;
		}
		void update(int p){
			t[p].val=t[rs(p)].val?t[rs(p)].val:t[ls(p)].val;
		}
		/*因为按dep排过序,所以只要找重链上最靠下的被管辖的点,靠上的点不可能被当前点管辖,即为右儿子的值*/
		void pushdown(int p){
			if (t[p].tag==-1) return;
			change(ls(p),t[p].tag);
			change(rs(p),t[p].tag);
			t[p].tag=-1;
		}
		void Modify(int fir,int lst,int val,int p=1,int L=1,int R=N){
			if (fir==L && lst==R) return change(p,val);
			pushdown(p);
			int mid=(L+R)>>1;
			if (lst<=mid) Modify(fir,lst,val,ls(p),L,mid);
			else if (fir>mid) Modify(fir,lst,val,rs(p),mid+1,R);
			else Modify(fir,mid,val,ls(p),L,mid),Modify(mid+1,lst,val,rs(p),mid+1,R);
			update(p);
		}
		int Query(int fir,int lst,int p=1,int L=1,int R=N){
			if (fir==L && lst==R) return t[p].val;
			pushdown(p);
			int mid=(L+R)>>1;
			if (lst<=mid) return Query(fir,lst,ls(p),L,mid);
			else if (fir>mid) return Query(fir,lst,rs(p),mid+1,R);
			else{
				int Lv=Query(fir,mid,ls(p),L,mid);
				int Rv=Query(mid+1,lst,rs(p),mid+1,R);
				return Rv?Rv:Lv;
			}
		}
	}SEG;
	
	int LCA(int x,int y){
		if (dep[x]<dep[y]) swap(x,y);
		for (int i; dep[x]>dep[y]; x=st[i][x])
			for (i=0; dep[st[i+1][x]]>dep[y]; ++i);
		for (int i; x!=y; x=st[i][x],y=st[i][y])
			for (i=0; dep[st[i+1][x]]!=dep[st[i+1][y]]; ++i);
		return x;
	}
	void Dye(int l,int r,int x){
		for (; top[l]!=top[r]; r=fa[top[r]])
			SEG.Modify(newid[top[r]],newid[r],x);
		SEG.Modify(newid[l],newid[r],x);
	}
	/*把一条重链上L到R的点及其子树全部改为由x管辖*/
	int dis(int x,int y){
		int lca=LCA(x,y);
		return dep[x]+dep[y]-2*dep[lca];
	}
	
	void main(){
		dep[1]=1;
		dfs1(); dfs2();
			
		Q=READ.Int();
		for (int k=1; k<=Q; ++k){
			int q=READ.Int();
			for (int i=1; i<=q; ++i)
				in[i]=qs[i]=READ.Int(),ans[qs[i]]=0;
			sort(qs+1,qs+q+1,cmp);
			
			ans[qs[1]]=N; Dye(1,qs[1],qs[1]); 
			for (int i=2; i<=q; ++i){
				int x=qs[i],y;
				for (int tmp=x; !(y=SEG.Query(newid[top[tmp]],newid[tmp])); tmp=fa[top[tmp]]);
				/*找到被管辖的点*/
				int d=(dis(x,y)-(y<x))/2,tmp=x;
				/*dis-(y<x)是为了解决到x和y距离相等的问题*/
				for (int j; d; d-=1<<j,tmp=st[j][tmp])
					for (j=0; (1<<(j+1))<=d; ++j);
				Dye(tmp,x,x); ans[qs[i]]=size[tmp]; ans[y]-=ans[qs[i]];
			}
			for (int i=1; i<=q; ++i) printf("%d ", ans[in[i]]);
			cout<<endl;
			SEG.Modify(1,N,0);
		}
	}
}
namespace Ninit{
	void init(){
		N=READ.Int();
		for (int i=1; i<N; ++i){
			int a=READ.Int(),b=READ.Int();
			add(a,b);
		}
	}
}
int main(){
	freopen("worldtree.in","r",stdin);
	freopen("worldtree.out","w",stdout);
	READ.Init();
	Ninit::init();
	Nwork::main();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值