SDOI2013 森林

主席树,启发式合并LCA.
每个节点在其父节点的基础上建权值线段树。
对于询问(x,y),设lca为其最近公共祖先,则答案由val[x]+val[y]-val[lca]-val[fa[lca]]决定,这与静态区间第k小数的做法一致。
对于连接操作(x,y).我们用启发式合并维护倍增数组。启发式合并的同时,暴力构建主席树。

虽然大家都是这么做的,但我不明白为什么人家代码3k,而我的代码有5k,而且还跑得很慢.

code:

#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<queue> 
using namespace std; 
#define MAXN 105000 
#define MS 18 
#define MAXM 20000000 
int limit=0; 
struct node 
{ 
    int val,ls,rs; 
    node() 
    { 
        val=ls=rs=0; 
    } 
}tr[MAXM]; 
int root[MAXN],fa[MAXN][MS+2],val[MAXN],dep[MAXN]; 
int n,m,T,lstans,tot; 
void _r(int& x) 
{ 
    char c=getchar(); 
    while(c<'0'||c>'9') 
    { 
        c=getchar(); 
    } 
    for(x=0;c>='0'&&c<='9';c=getchar()) 
    { 
        x=(x<<1)+(x<<3)+c-'0'; 
    } 
    return ; 
} 
void ins(int& p,int f,int l,int r,int x) 
{ 
    p=++tot; 
    tr[p]=tr[f]; 
    tr[p].val++; 
    if(l<r) 
    { 
        int mid=(l+r)>>1; 
        if(x<=mid) 
        { 
            ins(tr[p].ls,tr[f].ls,l,mid,x); 
        } 
        else 
        { 
            ins(tr[p].rs,tr[f].rs,mid+1,r,x); 
        } 
    } 
    return ; 
} 
int qa,qb,qc,qd; 
int query(int l,int r,int k) 
{ 
    if(l==r) 
    { 
        return l; 
    } 
    int num,mid=(l+r)>>1; 
    int a=tr[qa].ls,b=tr[qb].ls,c=tr[qc].ls,d=tr[qd].ls; 
    num=tr[a].val+tr[b].val-tr[c].val-tr[d].val; 
    if(k<=num) 
    { 
        qa=a; 
        qb=b; 
        qc=c; 
        qd=d; 
        return query(l,mid,k); 
    } 
    else 
    { 
        qa=tr[qa].rs; 
        qb=tr[qb].rs; 
        qc=tr[qc].rs; 
        qd=tr[qd].rs; 
        return query(mid+1,r,k-num); 
    } 
} 
bool vis[MAXN]; 
queue<int>bfs; 
int Next[MAXN<<1],Last[MAXN],End[MAXN<<1],e=1; 
void add(int x,int y) 
{ 
    End[++e]=y; 
    Next[e]=Last[x]; 
    Last[x]=e; 
    return ; 
} 
int LCA(int x,int y) 
{ 
    if(dep[x]<dep[y]) 
    { 
        swap(x,y); 
    } 
    int k=dep[x]-dep[y]; 
    for(int i=MS;i>=0;i--) 
    { 
        if(k>>i&1) 
        { 
            x=fa[x][i]; 
        } 
    } 
    if(x==y) 
    { 
        return x; 
    } 
    for(int i=MS;i>=0;i--) 
    { 
        if(fa[x][i]!=fa[y][i]) 
        { 
            x=fa[x][i]; 
            y=fa[y][i]; 
        } 
    } 
    return fa[x][0]; 
} 
int o[15],oo; 
void _w(int x) 
{ 
    if(x==0) 
    { 
        putchar('0'); 
        return ; 
    } 
    for(oo=0;x;x/=10) 
    { 
        o[++oo]=x%10; 
    } 
    for(;oo;--oo) 
    { 
        putchar('0'+o[oo]); 
    } 
    return ; 
} 
int bl[MAXN],size[MAXN]; 
void link(int x,int y) 
{ 
    bfs.push(x); 
    fa[x][0]=y; 
    dep[x]=dep[y]+1;
    int u,v; 
    while(bfs.size()) 
    { 
        u=bfs.front(); 
        bfs.pop(); 
        bl[u]=bl[y]; 
        size[bl[y]]++; 
        for(int i=1;i<=MS;i++) 
        { 
            fa[u][i]=fa[fa[u][i-1]][i-1]; 
        } 
        ins(root[u],root[fa[u][0]],1,limit,val[u]); 
        for(int t=Last[u];t;t=Next[t])
        {
            v=End[t];
            if(v!=fa[u][0])
            {
                fa[v][0]=u;
                dep[v]=dep[u]+1;
                bfs.push(v);
            }
        }
    }
    add(x,y);
    add(y,x); 
    return ; 
} 
int main() 
{ 
    int testcase; 
    _r(testcase); 
    _r(n); 
    _r(m); 
    _r(T); 
    for(int i=1;i<=n;i++) 
    { 
        _r(val[i]); 
        limit=max(limit,val[i]); 
    } 
    for(int i=1,x,y;i<=m;i++) 
    { 
        _r(x); 
        _r(y); 
        add(x,y); 
        add(y,x); 
    } 
    for(int i=1;i<=n;i++) 
    { 
        int u,v; 
        if(!vis[i]) 
        { 
            bfs.push(i); 
            vis[i]=1; 
            ins(root[i],0,1,limit,val[i]); 
            while(bfs.size()) 
            { 
                u=bfs.front(); 
                bfs.pop(); 
                bl[u]=i; 
                size[i]++; 
                for(int t=Last[u];t;t=Next[t]) 
                { 
                    v=End[t]; 
                    if(!vis[v]) 
                    { 
                        vis[v]=1; 
                        bfs.push(v); 
                        fa[v][0]=u; 
                        ins(root[v],root[u],1,limit,val[v]); 
                        dep[v]=dep[u]+1; 
                        for(int k=1;k<=MS;k++) 
                        { 
                            fa[v][k]=fa[fa[v][k-1]][k-1]; 
                        } 
                    } 
                } 
            } 
        } 
    } 
    char s[3]; 
    for(int i=1,x,y,k;i<=T;i++) 
    { 
        scanf("%s",s); 
        if(s[0]=='Q') 
        { 
            _r(x); 
            _r(y); 
            _r(k); 
            x^=lstans; 
            y^=lstans; 
            k^=lstans; 
            int lca=LCA(x,y); 
            qa=root[x]; 
            qb=root[y]; 
            qc=root[lca]; 
            qd=root[fa[lca][0]]; 
            lstans=query(1,limit,k); 
            _w(lstans); 
            putchar('\n'); 
        } 
        else 
        { 
            _r(x); 
            _r(y); 
            x^=lstans; 
            y^=lstans; 
            if(size[bl[x]]>size[bl[y]]) 
            { 
                swap(x,y); 
            } 
            link(x,y); 
        } 
    } 
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值