[HNOI2016]树

https://www.luogu.org/problemnew/show/P3248

题解

模拟题意即可,把每次接过去的子树看做一个点,然后这个关系构成了一棵树。

大力倍增即可。

代码

#include<bits/stdc++.h>
#define N 100009
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
int p[20][N],deep[N],tot,head[N],n,m,q,tott,dep[N],pr[20][N],T[N],size[N],c[N],pre[N];
ll dis[20][N],noww,b[N],nowid;
inline ll rd(){
	ll x=0;char c=getchar();bool f=0;
	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
struct node{
	int l,r,size;
}tr[N*40];
struct edge{int n,to;}e[N<<1];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void ins(int &cnt,int l,int r,int id){
	cnt=++tott;
	tr[cnt].size++;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(mid>=id)ins(ls,l,mid,id);
	else ins(rs,mid+1,r,id);
}
int merge(int u,int v){
	if(!u||!v)return u^v;
	int cnt=++tott;
	tr[cnt].size=tr[u].size+tr[v].size;
	ls=merge(tr[u].l,tr[v].l);
	rs=merge(tr[u].r,tr[v].r);
	return cnt;
} 
inline int kth(int &cnt,int l,int r,int k){
	int mid=(l+r)>>1;
	if(l==r)return l;
	if(tr[ls].size<k)return kth(rs,mid+1,r,k-tr[ls].size);
	else return kth(ls,l,mid,k);
}
void dfs(int u,int fa){
	ins(T[u],1,n,u);
	size[u]=1;
	for(int i=1;(1<<i)<=deep[u];++i)p[i][u]=p[i-1][p[i-1][u]];
	for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
		int v=e[i].to;deep[v]=deep[u]+1;p[0][v]=u;
		dfs(v,u);
		size[u]+=size[v];
		T[u]=merge(T[u],T[v]); 
	}
}
inline int getlca(int u,int v){
	if(deep[u]<deep[v])swap(u,v);
	for(int i=19;i>=0;--i)if(deep[u]-(1<<i)>=deep[v])u=p[i][u];
	if(u==v)return u;
	for(int i=19;i>=0;--i)if(p[i][u]!=p[i][v])u=p[i][u],v=p[i][v];
	return p[0][u];
}
inline int jump(int x,int xx){
	for(int i=19;i>=0;--i)if((1<<i)&xx)x=pr[i][x];
	return x;
}
int main(){
	n=rd();m=rd();q=rd();
	ll u,v;
	for(int i=1;i<n;++i){
		u=rd();v=rd();
		add(u,v);add(v,u);
	}
	dfs(1,0);
	b[++b[0]]=1;noww=n;
	c[b[0]]=1;
	nowid=1;
	while(m--){
		u=rd();v=rd();
		b[++b[0]]=noww+1;noww+=size[u];
		c[b[0]]=u;
		int x=upper_bound(b+1,b+b[0]+1,v)-b-1;
		int preid=x,preroot=c[x],now=kth(T[preroot],1,n,v-b[x]+1);
		//preid 被接的树的编号 preroot 被接的树的根 now被接的节点编号 
	//	cout<<x<<" "<<preroot<<" "<<now<<" "<<preid<<" biu \n"; 
		++nowid;
		pr[0][nowid]=preid;dis[0][nowid]=deep[now]-deep[preroot]+1;dep[nowid]=dep[preid]+1;
		pre[nowid]=now;
		for(int i=1;(1<<i)<=dep[nowid];++i)pr[i][nowid]=pr[i-1][pr[i-1][nowid]],
		dis[i][nowid]=dis[i-1][nowid]+dis[i-1][pr[i-1][nowid]];
	}
	while(q--){
		u=rd();v=rd();
	    int x=upper_bound(b+1,b+b[0]+1,u)-b-1,y=upper_bound(b+1,b+b[0]+1,v)-b-1;
	    int now1=kth(T[c[x]],1,n,u-b[x]+1),now2=kth(T[c[y]],1,n,v-b[y]+1);
//		cout<<x<<" "<<y<<"  "<<now1<<" "<<now2<<endl;		
		ll ans=0;
        if(x==y){
        	ans=deep[now1]+deep[now2]-2*deep[getlca(now1,now2)];
        	printf("%lld\n",ans);
        	continue;
		}
		if(dep[x]<dep[y])swap(x,y),swap(now1,now2);
        int bu=dep[x]-dep[y];
        if(jump(x,bu)==y){
          int xx=bu-1;ans+=deep[now1]-deep[c[x]];		  
		  for(int i=19;i>=0;--i)if((1<<i)&xx){
			  ans+=dis[i][x];x=pr[i][x];
		  }
		  ans++;
		  ans+=deep[now2]+deep[pre[x]]-2*deep[getlca(now2,pre[x])];
		}
		else{
			ans+=deep[now1]-deep[c[x]]+deep[now2]-deep[c[y]];
			for(int i=19;i>=0;--i)if(dep[x]-(1<<i)>=dep[y]){
			  ans+=dis[i][x];x=pr[i][x];
		     }
			for(int i=19;i>=0;--i)if(pr[i][x]!=pr[i][y]){
				ans+=dis[i][x]+dis[i][y];x=pr[i][x];y=pr[i][y];
			}
			int dx=pre[x],dy=pre[y];ans+=2;
		//	cout<<x<<" "<<y<<" "<<pr[0][x]<<" "<<pr[0][y]<<"  ";
			ans+=deep[dx]+deep[dy]-2*deep[getlca(dx,dy)];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2019-05-08 09:26  comld  阅读(194)  评论(0编辑  收藏  举报