1. 程式人生 > 實用技巧 >Luogu P3248 [HNOI2016]樹

Luogu P3248 [HNOI2016]樹

來跟我一起說:陳指導是魔鬼,把一道我任務清單裡躺了兩年的題搬出來強制我寫了

但說實話可能昨天狀態挺好一下子就寫過了,沒有調成傻逼

但這題的思路其實很簡單,真·樹套樹即可

即每次進行復制操作時,將每次複製的子樹看做一個大點,這樣可以建立一個新的樹,我們稱為大樹

對於大樹上的點我們需要維護一些必要的資訊,比如這個某個節點\(i\)對應的小節點的編號區間\(L_i,R_i\),在模板樹上對應的複製點\(pre_i\),在大樹上懸掛在哪個小節點下面\(lst_i\)

根據這些我們可以完成一些操作:

  • getrt(x),表示找到小節點\(x\)對應的大樹上的節點是哪個,這個可以通過與\(L_i\)的大小關係來二分
  • getpre(x),表示找到小節點\(x\)對應的模板樹上的節點是哪個,由於每次標號相當於一個求子樹第\(k\)大的過程,我們可以DFS序+主席樹,也可以線段樹合併

然後接下來的思路就是倍增了,對於大樹上的每個節點我們記錄倍增的祖先和距離,最後將維護下模板樹上的距離關係即可

說起來挺容易的但細節挺多,尤其是倍增求答案的時候有很多種小情況要注意

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=100005,P=17;
int n,m,q,x,y;
namespace T1 //Template Tree
{
	struct edge
	{
		int to,nxt;
	}e[N<<1]; int head[N],cnt,rt[N],size[N];
	inline void addedge(CI x,CI y)
	{
		e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
		e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
	}
	class Segment_Tree
	{
		private:
			struct segment
			{
				int ch[2],size;
			}node[N*P<<2]; int tot;
			#define lc(x) node[x].ch[0]
			#define rc(x) node[x].ch[1]
			#define S(x) node[x].size
			#define TN CI l=1,CI r=n
		public:
			inline void modify(int& now,CI pos,TN)
			{
				now=++tot; ++S(now); if (l==r) return; int mid=l+r>>1;
				if (pos<=mid) modify(lc(now),pos,l,mid); else modify(rc(now),pos,mid+1,r);
			}
			inline int merge(CI x,CI y,TN)
			{
				if (!x||!y) return x|y; int now=++tot,mid=l+r>>1; S(now)=S(x)+S(y);
				lc(now)=merge(lc(x),lc(y),l,mid); rc(now)=merge(rc(x),rc(y),mid+1,r); return now;
			}
			inline int query(CI now,CI k,TN)
			{
				if (l==r) return l; int mid=l+r>>1; if (k<=S(lc(now)))
				return query(lc(now),k,l,mid); else return query(rc(now),k-S(lc(now)),mid+1,r);
			}
			#undef lc
			#undef rc
			#undef S
			#undef TN
	}SEG;
	#define to e[i].to
	class Tree_Distance_Solver
	{
		private:
			int anc[N][P],dep[N];
			inline int getlca(int x,int y)
			{
				RI i; if (dep[x]<dep[y]) swap(x,y);
				for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
				if (x==y) return x; for (i=P-1;~i;--i)
				if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
				return anc[x][0];
			}
		public:
			inline void DFS(CI now=1,CI fa=0)
			{
				RI i; SEG.modify(rt[now],now); size[now]=1;
				for (anc[now][0]=fa,i=0;i<P-1;++i)
				if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
				for (dep[now]=dep[fa]+1,i=head[now];i;i=e[i].nxt)
				if (to!=fa) DFS(to,now),size[now]+=size[to],
				rt[now]=SEG.merge(rt[now],rt[to]);
			}
			inline int getdis(CI x,CI y)
			{
				return dep[x]+dep[y]-(dep[getlca(x,y)]<<1LL);
			}
	}T;
};
namespace T2 //Big Tree
{
	int pre[N],lst[N],L[N],R[N],tot,num,anc[N][P],dep[N],dis[N][P];
	inline int getrt(CI x)
	{
		int l=1,r=num,mid,ret; while (l<=r)
		if (L[mid=l+r>>1]<=x) ret=mid,l=mid+1; else r=mid-1; return ret;
	}
	inline int getpre(CI x)
	{
		int rt=getrt(x); return T1::SEG.query(T1::rt[pre[rt]],x-L[rt]+1);
	}
	inline void relink(CI x,CI y)
	{
		int rt=getrt(y); dep[++num]=dep[rt]+1; lst[num]=y; pre[num]=x;
		L[num]=tot+1; R[num]=tot+T1::size[x]; tot+=T1::size[x];
		anc[num][0]=rt; dis[num][0]=T1::T.getdis(getpre(y),pre[rt])+1;
		for (RI i=0;i<P-1;++i) if (anc[num][i])
		dis[num][i+1]=dis[num][i]+dis[anc[num][i]][i],
		anc[num][i+1]=anc[anc[num][i]][i]; else break;
	}
	inline int query(int x,int y,int ret=0)
	{
		int rx=getrt(x),ry=getrt(y); RI i;
		if (rx==ry) return T1::T.getdis(getpre(x),getpre(y));
		if (dep[rx]<dep[ry]) swap(x,y),swap(rx,ry);
		ret+=T1::T.getdis(getpre(x),pre[rx]); x=rx;
		for (i=P-1;~i;--i) if (dep[anc[x][i]]>dep[ry]) ret+=dis[x][i],x=anc[x][i];
		if (getrt(lst[x])==ry) return ret+1+T1::T.getdis(getpre(lst[x]),getpre(y));
		ret+=T1::T.getdis(getpre(y),pre[ry]); y=ry;
		if (dep[x]>dep[y]) ret+=dis[x][0],x=anc[x][0];
		for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
		ret+=dis[x][i]+dis[y][i],x=anc[x][i],y=anc[y][i];
		return ret+T1::T.getdis(getpre(lst[x]),getpre(lst[y]))+2;
	}
};
signed main()
{
	//reopen("treeman.in","r",stdin); freopen("treeman.out","w",stdout);
	RI i; for (scanf("%lld%lld%lld",&n,&m,&q),i=1;i<n;++i)
	scanf("%lld%lld",&x,&y),T1::addedge(x,y);
	T2::L[1]=T2::pre[1]=T2::num=1; T2::tot=T2::R[1]=n;
	for (T1::T.DFS(),i=1;i<=m;++i) scanf("%lld%lld",&x,&y),T2::relink(x,y);
	for (i=1;i<=q;++i) scanf("%lld%lld",&x,&y),printf("%lld\n",T2::query(x,y));
	return 0;
}