BZOJ 3123: [Sdoi2013]森林

8 篇文章 0 订阅
6 篇文章 0 订阅

其实直接主席树+树上启发式合并就好了

一道54% RE率的题,我也光荣地贡献了好多发RE。。。
坑点还是有不少 比如那是数据编号。。还有就是记得重构的时候lca要清空QAQ

#include<bits/stdc++.h>
using namespace std;
const int N=8e4+2;
char B[1<<14],*S=B,*T=B;
#define gc (S==T&&(T=(S=B)+fread(B,1,1<<14,stdin),S==T)?-1:*S++)
inline int read(){
    int x=0,f=1; char ch=gc;
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=gc;}
    while(ch>='0' && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=gc;}
    return x*f;
}
struct edge{
    int y,nex;
}a[N<<1]; int len,fir[N];
void ins(int x,int y){
    a[++len]=(edge){y,fir[x]},fir[x]=len;
}
int z[N],zl,qz[N],f[N],sz[N],fa[N][17],dep[N];
int Fa(int x){return x==f[x]?x:f[x]=Fa(f[x]);}
void merge(int x,int y){x=Fa(x),y=Fa(y),sz[y]+=sz[x],f[x]=y;}
int rt[N],lc[N*120],rc[N*120],s[N*120],tot;
void add(int &x,int y,int l,int r,int k){
    x=++tot,s[x]=s[y]+1;
    if(l==r)return;
    int mid=(l+r)>>1; lc[x]=lc[y],rc[x]=rc[y];
    if(k<=mid) add(lc[x],lc[y],l,mid,k);
    else add(rc[x],rc[y],mid+1,r,k);
}
int query(int l,int r,int k,int A,int B,int C,int D){
    if(l==r)return l;
    int mid=(l+r)>>1,u=0;
    u=s[lc[A]]+s[lc[B]]-s[lc[C]]-s[lc[D]];
    if(u>=k) return query(l,mid,k,lc[A],lc[B],lc[C],lc[D]);
    return query(mid+1,r,k-u,rc[A],rc[B],rc[C],rc[D]);
}
void dfs(int x,int FA){
    add(rt[x],rt[FA],1,zl,qz[x]);
    for(int i=1;i<17;++i)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int k=fir[x];k;k=a[k].nex){
        int y=a[k].y; if(y==FA)continue;
        dep[y]=dep[x]+1,fa[y][0]=x;
        dfs(y,x);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y]) x^=y^=x^=y;
    for(int i=16;~i;--i) if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=16;~i;--i) if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void link(int x,int y){
    ins(x,y),ins(y,x);
    int fx=Fa(x),fy=Fa(y);
    if(sz[fx]>sz[fy])
        fa[y][0]=x,dep[y]=dep[x]+1,dfs(y,x);
    else
        fa[x][0]=y,dep[x]=dep[y]+1,dfs(x,y);
    merge(x,y);
}
int main(){
    int i,te=read(),n=read(),m=read(),q=read();
    for(i=1;i<=n;++i) z[i]=qz[i]=read();
    sort(z+1,z+1+n); zl=unique(z+1,z+1+n)-z-1;
    for(i=1;i<=n;++i) qz[i]=lower_bound(z+1,z+1+zl,qz[i])-z;
    for(i=1;i<=n;++i) f[i]=i,sz[i]=1;
    int x,y,k;
    for(i=1;i<=m;++i)
        x=read(),y=read(),ins(x,y),ins(y,x),merge(x,y);
    for(i=1;i<=n;++i) if(!rt[i])dep[i]=1,dfs(i,0);
    int la=0,t;
    while(q--){
        char c=gc; while(c!='Q' && c!='L')c=gc;
        x=read()^la,y=read()^la;
        if(c=='Q'){
            k=read()^la,t=lca(x,y);
            printf("%d\n",la=z[ query(1,zl,k,rt[x],rt[y],rt[t],rt[fa[t][0]]) ]);
        }
        else link(x,y);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值