P2633|主席树+dfs序+树链剖分求lca+离散化

Posted fisherss

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2633|主席树+dfs序+树链剖分求lca+离散化相关的知识,希望对你有一定的参考价值。

不知道为什么会RE。。
待补

思路:链上求u和v两点路径第k小利用lca就转变为了 U+V-LCA-FA(LCA) 上的第k小,这因为每个点的主席树的root是从其父转移来的。可以用树链剖分求lca;在dfs序上建立主席树将树上问题转变为区间问题,询问的时候用主席树求区间k小值。

终于能写出这种题了,开心!

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5+100;
int n,m,e = 1,num,ans=0;
int mp[maxn],b[maxn],root[maxn]; 
vector<int> g[maxn];
/*  父亲,    深度,       子节点数, 重儿子,   dfs序,   dfs映射,链头,    链尾    */
int fa[maxn],depth[maxn],sz[maxn],son[maxn],id[maxn],rk[maxn],top[maxn],bot[maxn];
int cnt = 0;

struct Node{int v,lc,rc;}T[maxn*24];
struct A{
    int x,idx;
    bool operator < (const A &temp)const{
        return x < temp.x;
    }
}a[maxn];
int ranks[maxn];

//树链剖分求lca部分 
void dfs1(int x,int deep){
    depth[x] = deep;
    sz[x] = 1;
    for(int li = 0;li<g[x].size();li++){
        int i = g[x][li];
        if(i == fa[x]) continue;
        fa[i] = x;
        dfs1(i,deep+1);
        sz[x] += sz[i];
        if(sz[i] > sz[son[x]]) son[x] = i;
    }
}

void dfs2(int x,int tp){
    top[x] = tp;
    id[x] = ++cnt;
    rk[cnt] = x;
    if(son[x]) dfs2(son[x],tp),bot[x] = bot[son[x]];
    else bot[x] = x;
    for(int li=0;li<g[x].size();li++){
        int i = g[x][li];
        if(i != fa[x] && i != son[x])
            dfs2(i,i);
    }
}

int lca(int u,int v){
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u,v);
        u = fa[top[u]];
    }
    if(depth[u] < depth[v]) return u;
    return v;
}

//主席树插入insert 
void insert(int pre,int cur,int pos,int l,int r){
    if(l == r){
        T[cur].v = T[pre].v + 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid){
        T[cur].lc = ++e;
        T[cur].rc = T[pre].rc;
        insert(T[pre].lc,T[cur].lc,pos,l,mid);
    }else{
        T[cur].rc = ++e;
        T[cur].lc = T[pre].lc;
        insert(T[pre].rc,T[cur].rc,pos,mid+1,r);
    }
    T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
}

//主席树求第k小 
int kth(int ql,int qr,int LCA,int FLCA,int k,int l,int r){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = T[T[ql].lc].v + T[T[qr].lc].v + T[T[LCA].lc].v - T[T[FLCA].lc].v;
    if(sum >= k) return kth(T[ql].lc,T[qr].lc,T[LCA].lc,T[FLCA].lc,k,l,mid);
    else return kth(T[ql].rc,T[qr].rc,T[LCA].rc,T[FLCA].rc,k-sum,mid+1,r);
}

//dfs序上建树 
void dfs(int u){
    root[u] = ++e;
    insert(root[fa[u]],root[u],b[u],1,num);
    for(int i=0;i<g[u].size();i++){
        int v = g[u][i];
        if(v!=fa[u]) dfs(v);
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i].x;
        a[i].idx = i;
    }
    //离散化 
    sort(a+1,a+n+1);
    b[a[1].x] = ++num;
    mp[num] = a[1].x;
    for(int i=2;i<=n;i++){
        if(a[i].x != a[i-1].x) ++num;
        b[a[i].idx] = num; //b数组保存新的编号 
        mp[num] = a[i].x; //mp数组存储原始权值 
    }
    int u,v,k;
    for(int i=1;i<n;i++){
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1,1);
    dfs2(1,1);
    dfs(1);
    while(m--){
        cin>>u>>v>>k;
        u ^= ans;
        int LCA = lca(u,v);
        ans = mp[kth(root[u],root[v],root[LCA],root[fa[LCA]],k,1,num)];
        cout<<ans<<endl;
    }
    return 0;
} 

以上是关于P2633|主席树+dfs序+树链剖分求lca+离散化的主要内容,如果未能解决你的问题,请参考以下文章

模板树链剖分求LCA

树链剖分求LCA

树链剖分洛谷P3379 树链剖分求LCA

树链剖分简(单)介(绍)

树链剖分求LCA

Codechef FIBTREE 树链剖分 主席树 LCA 二次剩余 快速幂