[SDOI2013]森林

Posted skylee03

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2013]森林相关的知识,希望对你有一定的参考价值。

[SDOI2013]森林

题目大意:

一个(n(nle8 imes10^4))个点的森林,每个结点有一个权值(w_i)(q(qle8 imes10^4))次操作,操作包含以下两种:

  1. 查询(x)(y)的路径中,第(k)小的权值是多少;
  2. 连接(x)(y)

思路:

如果本来就是一棵树,那么显然可以用主席树来维护。

由于现在是一个森林,那么每次将较小的树合并到较大的树上,并重构较小的子树对应的主席树,同时重新维护倍增LCA的相关信息。

空间上注意内存回收。

时间复杂度(mathcal O(nlog^2n))

源代码:

#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
inline char getalpha() {
    register char ch;
    while(!isalpha(ch=getchar()));
    return ch;
}
const int N=8e4+1,logN=18;
int w[N],tmp[N],dep[N],anc[N][logN];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
    e[u].push_back(v);
    e[v].push_back(u);
}
inline int lg2(const float &x) {
    return ((unsigned&)x>>23&255)-127;
}
inline int lca(int x,int y) {
    if(dep[x]<dep[y]) std::swap(x,y);
    for(register int i=lg2(dep[x]-dep[y]);i>=0;i--) {
        if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
    }
    for(register int i=lg2(dep[x]);i>=0;i--) {
        if(anc[x][i]!=anc[y][i]) {
            x=anc[x][i];
            y=anc[y][i];
        }
    }
    return x==y?x:anc[x][0];
}
class DisjointSet {
    private:
        int anc[N],size[N];
        int find(const int &x) {
            return x==anc[x]?x:anc[x]=find(anc[x]);
        }
    public:
        void reset(const int &n) {
            std::fill(&size[1],&size[n]+1,1);
            for(register int i=1;i<=n;i++) anc[i]=i;
        }
        int count(const int &x) {
            return size[find(x)];
        }
        void merge(const int &x,const int &y) {
            int p=find(x),q=find(y);
            if(size[p]>size[q]) std::swap(p,q);
            anc[p]=q;
            size[q]+=size[p];
        }
};
DisjointSet s;
class FotileTree {
    #define mid ((b+e)>>1)
    private:
        struct Node {
            int val,left,right;
        };
        Node node[N*logN];
        std::deque<int> q;
        int new_node(const int &p) {
            const int ret=q.front();
            q.pop_front();
            node[ret]=node[p];
            return ret;
        }
        void del_node(const int &p) {
            q.push_back(p);
        }
    public:
        int root[N];
        FotileTree() {
            for(register int i=1;i<N*logN;i++) {
                q.push_back(i);
            }
        }
        void insert(int &p,const int &b,const int &e,const int &x) {
            p=new_node(p);
            node[p].val++;
            if(b==e) return;
            if(x<=mid) insert(node[p].left,b,mid,x);
            if(x>mid) insert(node[p].right,mid+1,e,x);
        }
        void erase(const int &p,const int &b,const int &e,const int &x) {
            del_node(p);
            if(b==e) return;
            if(x<=mid) erase(node[p].left,b,mid,x);
            if(x>mid) erase(node[p].right,mid+1,e,x);
        }
        int query(const int &p,const int &q,const int &r,const int &s,const int &b,const int &e,const int &k) const {
            if(b==e) return b;
            int tmp=0;
            tmp+=node[node[p].left].val;
            tmp+=node[node[q].left].val;
            tmp-=node[node[r].left].val;
            tmp-=node[node[s].left].val;
            if(tmp>=k) return query(node[p].left,node[q].left,node[r].left,node[s].left,b,mid,k);
            return query(node[p].right,node[q].right,node[r].right,node[s].right,mid+1,e,k-tmp);
        }
    #undef mid
};
FotileTree t;
void dfs(const int &x,const int &par) {
    memset(anc[x],0,sizeof anc[x]);
    anc[x][0]=par;
    dep[x]=dep[par]+1;
    for(register int i=1;i<=lg2(dep[x]);i++) {
        anc[x][i]=anc[anc[x][i-1]][i-1];
    }
    t.erase(t.root[x],1,tmp[0],w[x]);
    t.insert(t.root[x]=t.root[par],1,tmp[0],w[x]);
    for(unsigned i=0;i<e[x].size();i++) {
        const int &y=e[x][i];
        if(y==par) continue;
        dfs(y,x);
    }
}
inline void link(int x,int y) {
    if(s.count(x)>s.count(y)) {
        std::swap(x,y);
    }
    dfs(x,y);
    s.merge(x,y);
    add_edge(x,y);
}
inline int query(const int &x,const int &y,const int &k) {
    const int z=lca(x,y),w=anc[z][0];
    return tmp[t.query(t.root[x],t.root[y],t.root[z],t.root[w],1,tmp[0],k)];
}
int main() {
    getint();
    const int n=getint(),m=getint(),q=getint();
    for(register int i=1;i<=n;i++) {
        tmp[i]=w[i]=getint();
    }
    std::sort(&tmp[1],&tmp[n]+1);
    tmp[0]=std::unique(&tmp[1],&tmp[n]+1)-&tmp[1];
    for(register int i=1;i<=n;i++) {
        w[i]=std::lower_bound(&tmp[1],&tmp[tmp[0]]+1,w[i])-tmp;
        dep[i]=1;
        t.insert(t.root[i],1,tmp[0],w[i]);
    }
    s.reset(n);
    for(register int i=0;i<m;i++) {
        link(getint(),getint());
    }
    for(register int i=0,ans=0;i<q;i++) {
        const char opt=getalpha();
        const int x=getint()^ans,y=getint()^ans;
        if(opt=='Q') {
            printf("%d
",ans=query(x,y,getint()^ans));
        }
        if(opt=='L') link(x,y);
    }
    return 0;
}

以上是关于[SDOI2013]森林的主要内容,如果未能解决你的问题,请参考以下文章

「Luogu P3302」[SDOI2013]森林

[SDOI2013]森林

SDOI2013 森林

[SDOI2013]森林(树上主席树)

P3302 SDOI2013森林

[SDOI2013]森林