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;
}
posted @ 2020-10-07 11:07  空気力学の詩  阅读(120)  评论(0编辑  收藏  举报