zoukankan      html  css  js  c++  java
  • 【洛谷5903】【模板】树上 k 级祖先(长链剖分)

    点此看题面

    大致题意: 给定一棵树,多组询问求(k)级祖先。

    长链剖分

    长链剖分板子题,通过(O(logn))的预处理,来(O(1))求出(k)级祖先。

    具体做法

    首先我们要知道一个性质:任意一个点(k)级祖先所在长链长度大于等于(k)

    证明:假设长度小于(k),则从该祖先到该节点的链的长度就大于这条长链的长度,与长链的定义不符,故假设不成立,原命题得证。

    接下来,我们一开始先通过倍增,求出(fa_{i,j})表示(i)(2^j)级祖先。

    而与此同时,对于每条长链的顶点(t),我们依次用两个数组(u)(d)存下其(len(t))个祖先节点和长链上的所有节点(也是(len(t))个)。

    然后,求答案时,设(k)的最高位为第(hb)位((HighestBit)),则我们先找到(x)(2^{hb})级祖先(f),并设(top_f)(f)所在长链的顶点。

    根据先前证明的性质,(len(top_f)ge2^{hb})(2 imes 2^{hb})必然大于(k),所以(2 imes len(top_f))也必然大于(k),故可证明必然能在(top_f)(u)(d)数组中找到(x)(k)级祖先。

    下一步,我们比较(dep_f-dep_{top_f})(k xor 2^{hb}),如果(dep_f-dep_{top_f}ge k xor 2^{hb}),说明最终答案在这条长链上,否则说明是顶点的祖先。

    如果在这条长链上,返回(d_{top_f,(dep_f-dep_{top_f})-(k xor 2^{hb})}),否则返回(u_{top_f,(k xor 2^{hb})-(dep_f-dep_{top_f})})

    具体实现中,由于每条长链只有链首需要开两个大小为链长的数组,因此所有(u)(d)完全可以存在一个数组中,只要记下每一个链首的数组在总数组中对应的位置(其实就是(dfs)序)即可查询。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define LN 20
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define U unsigned
    using namespace std;
    int n,rt,ee,lnk[N+5];U base;struct edge {int to,nxt;}e[N<<1];
    I U Get(U x) {return x^=x<<13,x^=x>>17,x^=x<<5,base=x;}
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		#undef D
    }F;
    class LongChainDissection
    {
    	private:
    		int cnt,f[N+5][LN+5],g[N+5],D[N+5],len[N+5],tp[N+5],hb[N+5],p[N+5],u[N+5],d[N+5];
    		I void dfs1(CI x)//第一遍dfs
    		{
    			RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];
    			for(i=lnk[x];i;i=e[i].nxt) D[e[i].to]=D[f[e[i].to][0]=x]+1,
    				dfs1(e[i].to),len[e[i].to]>len[g[x]]&&(g[x]=e[i].to);len[x]=len[g[x]]+1;//求出长儿子
    		}
    		I void dfs2(CI x,RI t,CI y=0)//第二遍dfs
    		{
    			RI i;p[x]=++cnt,d[p[x]]=x,u[p[x]]=y,tp[x]=t,g[x]&&(dfs2(g[x],t,f[y][0]),0);//求出u和d,先处理长儿子
    			for(i=lnk[x];i;i=e[i].nxt) e[i].to^g[x]&&(dfs2(e[i].to,e[i].to,e[i].to),0);//处理其他儿子
    		}
    	public:
    		I int operator [] (CI x) {return D[x];}I void Init()
    		{
    			D[rt]=1,dfs1(rt),dfs2(rt,rt);
    			for(RI i=1,t=-1;i<=n;++i) (i>>t+1)&1&&++t,hb[i]=t;//预处理HighestBit
    		}
    		I int Anc(RI x,RI y)//求x的y级祖先
    		{
    			if(!y) return x;if(x=f[x][hb[y]],!(y^=1<<hb[y])) return x;//上跳
    			RI t=D[x]-D[tp[x]];return t>=y?d[p[tp[x]]+t-y]:u[p[tp[x]]+y-t];//分二类讨论
    		}
    }D;
    int main()
    {
    	RI Qt,i,x,y,lst=0;for(F.read(n,Qt,base),i=1;i<=n;++i) F.read(x),x?add(x,i):(rt=i);//读入
    	long long ans=0;for(D.Init(),i=1;i<=Qt;++i) x=(Get(base)^lst)%n+1,//处理询问
    		y=(Get(base)^lst)%D[x],ans^=1LL*i*(lst=D.Anc(x,y));return printf("%lld
    ",ans),0;
    }
    
  • 相关阅读:
    MT【38】与砝码有关的两个题
    MT【37】二次函数与整系数有关的题
    MT【36】反函数有关的一道题
    MT【35】用复数得到的两组恒等式
    MT【34】正余弦的正整数幂次快速表示成正余弦的线性组合
    MT【33】证明琴生不等式
    MT【32】内外圆(Apollonius Circle)的几何证明
    MT【31】傅里叶级数为背景的三角求和
    MT【30】椭圆的第二定义解题
    MT【29】介绍向量的外积及应用举例
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5903.html
Copyright © 2011-2022 走看看