BZOJ4535 [Hnoi2016]树

16 篇文章 0 订阅
5 篇文章 0 订阅

考虑每次复制操作,我们只需要知道复制这个子树的根以及原树就能知道新树里这一部分的形态,所以我们把每次复制操作复制的子树和最开始的一颗树都缩点,给每个缩点记录在原树里的根,父亲在原树里的编号。每个缩点和父亲缩点的距离为父亲在原树里与缩点父亲的根在原树里的距离+1,也就是这个缩点树的根到上一个缩点树的根在新树里的距离,维护原树和缩点树两个树倍增结构,询问的时候根据LCA在缩点树里的位置分类讨论即可

为了把新的编号和原树上的编号对应我们需要在原树上主席树查子树K小,强套主席树,差评

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define MAXN 100010
#define MAXM 2000010
#define ll long long
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
struct vec{
	int to;
	int fro;
};
struct mul{
	int fa[MAXN][20];
	int dep[MAXN];
	ll dis[MAXN];
	void ins(int x,int y,int z);
	int acs(int x,int y);
	int lca(int x,int y);
	ll D(int x,int y);
};
int n,m,q;
mul t1,t2;
int dfn[MAXN],tim;
int rt[MAXN];
int son[MAXM][2],Siz[MAXM],tot;
int siz[MAXN];
vec mp[MAXN*2];
int tai[MAXN],cnt;
ll st[MAXN];
int sz[MAXN],r[MAXN],c[MAXN],N;
inline void be(int x,int y){
	mp[++cnt].to=y;
	mp[cnt].fro=tai[x];
	tai[x]=cnt;
}
inline void bde(int x,int y){
	be(x,y);
	be(y,x);
}
void change(int &x,int xx,int l,int r,int p){
	x=++tot;
	memcpy(son[x],son[xx],sizeof(son[x]));
	Siz[x]=Siz[xx]+1;
	if(l==r){
		return ;
	}
	int mid=l+r>>1;
	if(p<=mid){
		change(son[x][0],son[xx][0],l,mid,p);
	}else{
		change(son[x][1],son[xx][1],mid+1,r,p);
	}
}
void mul::ins(int x,int y,int z){
	int j,t;
	dis[y]=dis[x]+z;
	dep[y]=dep[x]+1;
	for(j=1,t=x;t;j++){
		fa[y][j]=t;
		t=fa[t][j];
	}
}
int mul::lca(int x,int y){
	int i;
	if(dep[x]<dep[y]){
		swap(x,y);
	}
	for(i=19;i;i--){
		if(dep[fa[x][i]]>=dep[y]){
			x=fa[x][i];
		}
	}
	for(i=19;i;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return x==y?x:fa[x][1];
}
ll mul::D(int x,int y){
	int z=lca(x,y);
	return dis[x]+dis[y]-2*dis[z];
}
int mul::acs(int x,int y){
	int k=1;
	while(y){
		if(y&1){
			x=fa[x][k];
		}
		k++;
		y>>=1;
	}
	return x;
}
void dfs(int x){
	int i,j,y,t;
	dfn[x]=++tim;
	siz[x]=1;
	change(rt[tim],rt[tim-1],1,n,x);
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(!dfn[y]){
			t1.ins(x,y,1);
			dfs(y);
			siz[x]+=siz[y];
		}
	}
}
int bel(ll x){
	int re;
	int l=1,r=N;
	while(l<=r){
		int mid=l+r>>1;
		if(st[mid]<=x){
			re=mid;
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	return re;
}
int kth(int x,int xx,int l,int r,int k){
	if(l==r){
		return l;
	}
	int t=Siz[son[xx][0]]-Siz[son[x][0]];
	int mid=l+r>>1;
	if(k<=t){
		return kth(son[x][0],son[xx][0],l,mid,k);
	}else{
		return kth(son[x][1],son[xx][1],mid+1,r,k-t);
	}
}
int find(ll x,int y){
	return kth(rt[dfn[r[y]]-1],rt[dfn[r[y]]+siz[r[y]]-1],1,n,x-st[y]+1);
}
int main(){
	/*
	freopen("tree13.in","r",stdin);
	freopen("aw.out","w",stdout);
	//*/
	int i,xx,yy,z,zz,bx,by;
	ll x,y;
	scanf("%d%d%d",&n,&m,&q);
	for(i=1;i<n;i++){
		scanf("%lld%lld",&x,&y);
		bde(x,y);
	}
	t1.dep[1]=t2.dep[1]=1;
	dfs(1);
	st[++N]=1;
	sz[N]=n;
	r[N]=1;
	c[N]=0;
	for(i=1;i<=m;i++){
		scanf("%lld%lld",&x,&y);
		z=bel(y);
		yy=find(y,z);
		t2.ins(z,++N,t1.dis[yy]-t1.dis[r[z]]+1);
		st[N]=st[N-1]+sz[N-1];
		r[N]=x;
		sz[N]=siz[x];
		c[N]=yy;
	}
	for(i=1;i<=q;i++){
		scanf("%lld%lld",&x,&y);
		bx=bel(x);
		by=bel(y);
		xx=find(x,bx);
		yy=find(y,by);
		ll re=0;
		re+=t2.D(bx,by);
		int L=t2.lca(bx,by);
		if(bx==L&&by==L){
			re+=t1.D(xx,yy);
		}else if(bx!=L&&by!=L){
			re+=t1.dis[xx]-t1.dis[r[bx]];
			xx=c[t2.acs(bx,t2.dep[bx]-t2.dep[L]-1)];
			re+=t1.dis[yy]-t1.dis[r[by]];
			yy=c[t2.acs(by,t2.dep[by]-t2.dep[L]-1)];
			re-=(t1.dis[t1.lca(xx,yy)]-t1.dis[r[L]])*2;
		}else if(L!=bx){
			re+=t1.dis[xx]-t1.dis[r[bx]];
			xx=c[t2.acs(bx,t2.dep[bx]-t2.dep[L]-1)];
			z=t1.lca(xx,yy);
			zz=r[L];
			re-=t1.dis[z]-t1.dis[zz];
			re+=t1.dis[yy]-t1.dis[z];
		}else{
			re+=t1.dis[yy]-t1.dis[r[by]];
			yy=c[t2.acs(by,t2.dep[by]-t2.dep[L]-1)];
			z=t1.lca(xx,yy);
			zz=r[L];
			re-=t1.dis[z]-t1.dis[zz];
			re+=t1.dis[xx]-t1.dis[z];
		}
		printf("%lld\n",re);
	}
	return 0;
}

/*
5 3 2
2 1
3 2
4 2
5 3

4 2
3 4
5 5

8 6
1 4

*/
</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值