zoukankan      html  css  js  c++  java
  • 长链剖分 解 k级祖先问题

    概念

    长链剖分, 和重链剖分一样是一种链剖分, 只不过重儿子变成了长儿子。

    称一个点的子树深度(这个概念是我yy的,在其他地方不一定适用)为:以这个点为根的子树中所有节点深度的最大值。
    一个点的长儿子就是这个点的儿子中子树深度最大的那个儿子。

    常见性质

    1.对树长链剖分后, 所有链的 (size) ( (length) ) 之和为树的节点个数。
    显然成立
    2.一个点的 (k) 级祖先所在链的长度 (geq k)
    若这个点的 (k) 祖先与其在同一链中, 则性质成立。
    若反之,则可以推出这个点的 (k) 祖先的儿子们的最大子树深度大于 (k), 性质成立。

    算法流程

    [妙的一匹 ---蒟蒻博主 ]

    前置定义
    定义 (highbit(n))(log_2(n)) 的整数部分。
    假设要求节点 (x)(k) 级祖先。
    (r = 2^{highbit(k)})

    真 · 流程
    求出 (x)(r) 级祖先 (y), 问题转化为求 (y)(k-r) 级祖先。(由于 (r)(2) 的整次幂, 所以可以倍增处理后 (O(1)) 回答)

    由二进制的性质, 可知 (k-r<r)
    常见性质2(y) 所在链的长度 (geq r)

    故预处理出每个链 (L) 链顶的 (1 leq k leq length(L)) 级祖先和(1 leq k leq length(L)) 级儿子, 可以保证 (y)(k-r) 级祖先必被包含其中。


    时间复杂度分析
    倍增预处理 (O(nlog n))
    预处理每个链 (L)(length(L)) 个祖先和儿子, 复杂度是 (O(sum_{L} length(L)) = O(n))
    显而易见, 经过预处理之后, 对某个 (k) 级祖先问题的回答是可以做到 (O(1)) 的。


    板子题
    AC代码

    // 注意, 以一个点 x 为顶端的链的长度为 treedep[x]-dep[x]+1;
    // 由于这个 sb 错误我调了很久qwq
    // 希望看这篇博文的人引以为戒 qwq
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 5e5+15;
    
    #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,rt;
    vector<int>e[maxn],ks[maxn],kf[maxn];
    int treedep[maxn],dep[maxn],son[maxn],top[maxn];
    int f[21][maxn];
    
    void po1(int x,int fa,int Dep) {
    	treedep[x]=dep[x]=Dep;
    	f[0][x]=fa;
    	for(int i=0;i<(int)e[x].size();++i) {
    		int y=e[x][i]; if(y==fa) continue;
    		po1(y,x,Dep+1);
    		if(treedep[y]>treedep[son[x]]) treedep[x]=treedep[son[x]=y];
    	}
    }
    void po2(int x,int tp) {
    	top[x]=tp;
    	if(son[x]) po2(son[x],tp);
    	for(int i=0;i<(int)e[x].size();++i) {
    		int y=e[x][i]; if(y==f[0][x]||y==son[x]) continue;
    		po2(y,y);
    	}
    }
    
    int highbit[maxn];
    int kfa(int x,int k) {
    	if(!k) return x;
    	int r=highbit[k];
    	x=f[r][x];
    	k-=(1<<r);
    	if(dep[x]-k<dep[top[x]])
    		return kf[top[x]][dep[top[x]]-(dep[x]-k)];
    	else
    		return ks[top[x]][(dep[x]-k)-dep[top[x]]];
    }
    
    int ans[5000005];
    int main()
    {
    	cin>>n>>q>>s;
    	for(int i=1;i<=n;++i) {
    		int ff;scanf("%d",&ff);
    		if(ff) e[ff].push_back(i);
    		else rt=i;
    	}
    	po1(rt,0,1);
    	po2(rt,rt);
    	for(int k=1;k<=20;++k)
    		for(int i=1;i<=n;++i)
    			f[k][i] = f[k-1][f[k-1][i]];
    	for(int x=1;x<=n;++x) if(x==top[x]) {
    		int ns=x, nf=x;
    		for(int i=0;i<=treedep[x]-dep[x]+1;++i) {
    			ks[x].push_back(ns), kf[x].push_back(nf);
    			ns=son[ns], nf=f[0][nf];
    		}
    	}
    	for(int i=1;i<=n;++i) highbit[i] = log2(i);
    	for(int i=1;i<=q;++i) {
    		int x=((get(s) xor ans[i-1]) % n)+1;
    		int k=(get(s) xor ans[i-1])%dep[x];
    		ans[i] = kfa(x,k);
    	}
    	long long ANS = 0ll;
    	for(int i=1;i<=q;++i) ANS ^= 1ll*i*ans[i];
    	cout << ANS;
    	return 0;
    }
    
  • 相关阅读:
    《代码整洁之道》三
    《代码整洁之道》二
    第五周总结
    第四周总结
    第三周总结
    第二周总结
    第一周总结
    课程总结
    周总结16
    河北省科技信息通用调查系统综合查询功能开发——Day9
  • 原文地址:https://www.cnblogs.com/tztqwq/p/12789673.html
Copyright © 2011-2022 走看看