长链剖分 O(1) 求树上 k 级祖先

长链剖分也是一种树上的链剖分的方法。与重链剖分不同,长链剖分对于树上的每个点,取子树深度最大的儿子,向它连重边,其他的儿子向它连轻边。容易发现一个点所在的重链的长度至少为它子树的深度。

利用这个性质可以\(O(nlogn)\)预处理,\(O(1)\)求树上任意节点的k级祖先。比如当前要询问点x的k级祖先(k>0),我们取k的二进制表示中最高的1,令这个1表示的是\(2^a\)。我们先用倍增(\(O(nlogn)\)预处理)求出x的\(2^a\)级祖先y。接下来需要求y的\(k-2^a\)级祖先。根据上面说的性质,y所在的重链一定至少有\(2^a\)个点,令这条链的长度为len,链中深度最浅的点为z。由于\(2^a>k-2^a\),所以y的\(k-2^a\)级祖先要么在这条链中,要么从z往父亲方向走最多\(2^a\)步一定能走到。为了\(O(1)\)询问,只要对每一条重链,对它最浅的点预处理出从它向上走len步形成的链,以及这条链本身的所有点就可以了。

洛谷模板

点击查看代码
#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

void fileio()
{
  #ifdef LGS
  freopen("in.txt","r",stdin);
  freopen("out.txt","w",stdout);
  #endif
}
void termin()
{
  #ifdef LGS
  std::cout<<"\n\nPROGRAM TERMINATED";
  #endif
  exit(0);
}

using namespace std;

#define ui unsigned int
ui s;

inline ui get(ui x) {
	x ^= x << 13;
	x ^= x >> 17;
	x ^= x << 5;
	return s = x; 
}

int n,q,root,dep[500010],mxd[500010],fa[500010][22],top[500010];
vector <int> g[500010],v[500010],vv[500010];

void dfs(int pos,int d)
{
  dep[pos]=mxd[pos]=d;
  pii lk=mpr(-1,-1);
  rep(i,g[pos].size())
  {
    fa[g[pos][i]][0]=pos;
    dfs(g[pos][i],d+1);
    mxd[pos]=max(mxd[pos],mxd[g[pos][i]]);
    lk=max(lk,mpr(mxd[g[pos][i]],g[pos][i]));
  }
  if(lk.fi>-1)
  {
    rep(i,g[pos].size()) if(g[pos][i]!=lk.se)
    {
      int len=mxd[g[pos][i]]-dep[g[pos][i]]+1,u=g[pos][i];
      rep(j,len)
      {
        v[g[pos][i]].pb(u);
        u=fa[u][0];if(u==0) break;
      }
    }
  }
}

void dfs2(int pos,int tp)
{
  if(v[pos].size()>0) tp=pos;
  top[pos]=tp;
  vv[tp].pb(pos);
  rep(i,g[pos].size()) dfs2(g[pos][i],tp);
}

int getAnces(int x,int k)
{
  int tp=top[x];
  if(k<=dep[x]-dep[tp])
  {
    int need=dep[x]-dep[tp]-k;
    return vv[tp][need];
  }
  else
  {
    int need=k-(dep[x]-dep[tp]);
    return v[tp][need];
  }
}

int main()
{
	fileio();

  cin>>n>>q>>s;
  int x;
  repn(i,n)
  {
    scanf("%d",&x);
    if(x==0) root=i;
    else g[x].pb(i);
  }
  dfs(root,1);
  v[root]={root};
  rep(i,20) repn(j,n) fa[j][i+1]=fa[fa[j][i]][i];
  dfs2(root,root);
  int lstans;
  LL ans=0;
  rep(qn,q)
  {
    int x=(get(s)^(ui)lstans)%n+1,k=(get(s)^(ui)lstans)%dep[x];
    if(k>0)
    {
      int lev=31-__builtin_clz(k);
      x=fa[x][lev];k^=(1<<lev);
    }
    lstans=getAnces(x,k);
    ans^=(LL)(qn+1)*lstans;
  }
  cout<<ans<<endl;
  
	termin();
}
posted @ 2022-10-27 17:51  LegendStane  阅读(397)  评论(0)    收藏  举报