森林「SDOI 2013」

题意

有一个森林,初始有一定的边。给定两种操作:

  1. 查询两点路径上k小值。

  2. 联通两点。

强制在线。


思路

维护主席树,合并的时候按照子树大小dfs暴力合。

代码

学校OJ AC BZOJ MLE Luogu MLE

(然而学校OJ空间限制比洛谷小一半???)

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

    template<typename T>inline void read (T &x) {
        x=0;T f=1;char c=getchar();
        for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
        for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
        x*=f;
    }

    template<typename T>inline void write (T x) {
        if (x<0) putchar('-'),x*=-1;
        if (x>=10) write(x/10);
        putchar(x%10+'0');
    }

}

using namespace StandardIO;

namespace Project {
    
    const int N=80001;
    
    int n,m,t,len,last;
    int a[N],b[N],fa[N],size[N];
    int cnt;
    int head[N];
    struct node {
        int to,next;
    } edge[N<<2];
    int dep[N],vis[N],f[N][17];
    int tot;
    int root[N];
    struct tnode {
        int ls,rs;
        int size;
    } tree[N*600];
    
    inline void add (int a,int b) {
        edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
    }
    int find (int x) {
        return (x==fa[x])?x:fa[x]=find(fa[x]);
    }
    void build (int l,int r,int &pos) {
        pos=++tot,tree[pos].size=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,tree[pos].ls),build(mid+1,r,tree[pos].rs);
    }
    void update (int l,int r,int x,int las,int &pos) {
        pos=++tot,tree[pos]=tree[las],++tree[pos].size;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) update(l,mid,x,tree[las].ls,tree[pos].ls);
        else update(mid+1,r,x,tree[las].rs,tree[pos].rs);
    }
    int query (int l,int r,int x,int o,int p,int q,int s) {
        if (l==r) return b[l];
        int t=tree[tree[o].ls].size+tree[tree[p].ls].size-tree[tree[q].ls].size-tree[tree[s].ls].size,mid=(l+r)>>1;
        if (x<=t) return query(l,mid,x,tree[o].ls,tree[p].ls,tree[q].ls,tree[s].ls);
        return query(mid+1,r,x-t,tree[o].rs,tree[p].rs,tree[q].rs,tree[s].rs);
    }
    void dfs (int now,int father,int rt) {
        ++size[rt],dep[now]=dep[father]+1,fa[now]=father,vis[now]=1,f[now][0]=father;
        for (register int i=1; i<=16; ++i) f[now][i]=f[f[now][i-1]][i-1];
        update(1,len,lower_bound(b+1,b+len+1,a[now])-b,root[father],root[now]);
        for (register int i=head[now]; i; i=edge[i].next) {
            int to=edge[i].to;
            if (to==father) continue;
            dfs(to,now,rt);
        }
    }
    inline int lca (int x,int y) {
        if (x==y) return x;
        if (dep[x]<dep[y]) swap(x,y);
        for (register int i=16; i>=0; --i) {
            if (dep[y]<=dep[f[x][i]]) x=f[x][i];
        }
        if (x==y) return x;
        for (register int i=16; i>=0; --i) {
            if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        }
        return f[x][0];
    }
    
    inline void MAIN () {
        read(n),read(n),read(m),read(t);
        for (register int i=1; i<=n; ++i) read(a[i]),b[i]=a[i],fa[i]=i;
        sort(b+1,b+n+1),len=unique(b+1,b+n+1)-b-1;
        for (register int i=1; i<=m; ++i) {
            int x,y;read(x),read(y);
            add(x,y),add(y,x);
        }
        build(1,len,root[0]);
        for (register int i=1; i<=n; ++i) {
            if (!vis[i]) dfs(i,0,i),fa[i]=i;
        }
        while (t--) {
            char op;int x,y,z;
            scanf("%c",&op),read(x),read(y),x^=last,y^=last;
            if (op=='Q') {
                read(z),z^=last;
                int tmp=lca(x,y);
                write(last=query(1,len,z,root[x],root[y],root[tmp],root[f[tmp][0]])),putchar('\n');
            } else {
                add(x,y),add(y,x);
                int fx=find(x),fy=find(y);
                if (size[fx]<size[fy]) swap(fx,fy),swap(x,y);
                dfs(y,x,fx);
            }
        }
    }

}

int main () {
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    Project::MAIN();
}

转载于:https://www.cnblogs.com/ilverene/p/11481528.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值