zoukankan      html  css  js  c++  java
  • 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
    ",T2::query(x,y));
    	return 0;
    }
    
  • 相关阅读:
    20172315 2018-2019-1 《程序设计与数据结构》第九周学习总结
    20172315 2018-2019-1 《程序设计与数据结构》实验二报告
    20172315 2018-2019-1 《程序设计与数据结构》第八周学习总结
    20172315 2018-2019-2 《程序设计与数据结构》第七周学习总结
    20172315 2018-2019-1 《程序设计与数据结构》第六周学习总结
    20172315 2018-2019-1 《程序设计与数据结构》第五周学习总结
    20172315 2018-2019-1 《程序设计与数据结构》第四周学习总结
    20172310 2018-2019-1《程序设计与数据结构》(下)课程总结
    Do-Now—团队 冲刺博客六
    Do-Now—团队冲刺博客三
  • 原文地址:https://www.cnblogs.com/cjjsb/p/13776660.html
Copyright © 2011-2022 走看看