洛谷P3348/UOJ#195/LOJ#2092/BZOJ#4573[ZJOI2016]大森林(LCT)

不得不说,ZJOI 的可做题都是数据结构题

 这个东西显然不能在线,所以考虑离线以后从左往右扫过去。同时询问可以把整棵树处理好再处理。

那么我们要处理的就是修改生长结点引发的孩子关系变化。

考虑对每个修改开一个虚点,然后每次生长出的点挂在最近的修改下,相邻修改前后相连,不难发现这种连法像多叉转二叉一样,不会改变实点距离。那么转换就是把对应虚点从它父亲上切下来,连到对应结点上。

这个 LCT 显然不能换根,因此要求 LCA,方法是先 access(u),然后 access(v),那么 v 跳到根碰到的最后一个点就是 LCA。

对于一些树上没有相应结点,判一下就可以了。

洛谷P3348/UOJ#195/LOJ#2092/BZOJ#4573[ZJOI2016]大森林(LCT)洛谷P3348/UOJ#195/LOJ#2092/BZOJ#4573[ZJOI2016]大森林(LCT)
#include<cstdio>
#include<vector>
#include<utility>
#define For(i,A,B) for(i=(A);i<=(B);++i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=100050;
const int BUF=1<<22;
char rB[BUF],*rS,*rT,wB[BUF+50],*wT=wB;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,BUF,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wT-wB,stdout);wT=wB;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=x*10+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    short l=-1;
    if(wT-wB>BUF)flush();
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    *wT++=x|48;
    while(l>=0)*wT++=buf[l--]|48;
    *wT++='\n';
}
int lb[N*2],rb[N*2],bl[N*2],f[N*2],ch[N*2][2],sum[N*2],ans[N*2];
bool val[N*2];
vector<pair<int,int> > L[N],Q[N];
vector<int> R[N],id[N];
inline void chkmin(int &a,int b){if(b<a)a=b;}
inline void chkmax(int &a,int b){if(a<b)a=b;}
inline bool nrt(int o){return ch[f[o]][0]==o||ch[f[o]][1]==o;}
inline bool dir(int o){return ch[f[o]][1]==o;}
inline void pushup(int o){sum[o]=val[o]+sum[ch[o][0]]+sum[ch[o][1]];}
inline void rot(int o){
    int fa=f[o];
    bool d=dir(o);
    f[o]=f[fa];
    if(nrt(fa))ch[f[o]=f[fa]][dir(fa)]=o;
    if(ch[fa][d]=ch[o][d^1])f[ch[fa][d]]=fa;
    f[ch[o][d^1]=fa]=o;
    pushup(fa);
}
inline void splay(int o){
    for(;nrt(o);rot(o))if(nrt(f[o]))rot((dir(o)^(dir(f[o])))?o:f[o]);
    pushup(o);
}
inline int access(int x){
    int y=0;
    for(;x;x=f[y=x]){
        splay(x);
        ch[x][1]=y;
        pushup(x);
    }
    return y;
}
inline void link(int x,int y){
    access(x);splay(x);
    f[x]=y;
}
inline void cut(int x){
    access(x);splay(x);
    f[ch[x][0]]=0;ch[x][0]=0;
//其实这个 cut 是错的,但因为 cut 完立即 link 时进行了 pushup,所以这里可以不用 pushup
}
inline int lca(int x,int y){
    int z,s;
    access(x);splay(x);s=sum[x];
    z=access(y);splay(y);s+=sum[y];
    access(z);splay(z);
    return s-sum[z]*2;
}
int main(){
    int n=rb[1]=rd(),m=rd(),i,j,opt,x,y,z,tot=1,cl=0,ql=0;
    bl[1]=lb[1]=1;
    For(i,1,m){
        opt=rd();x=rd();y=rd();
        if(!opt){
            bl[++tot]=cl;
            lb[tot]=x;rb[tot]=y;
        }else if(opt==1){
            chkmax(x,lb[z=rd()]);chkmin(y,rb[z]);
            if(x>y)continue;
            L[x].pb(mp(++cl,z));
            if(y<n)R[y+1].pb(cl);
        }else{Q[x].pb(mp(y,rd()));id[x].pb(++ql);}
    }
    For(i,1,tot){
        val[i]=1;
        if(i>1)link(i,!bl[i]?1:bl[i]+tot);
    }
    For(i,1,cl)link(i+tot,i>1?i-1+tot:1);
    For(i,1,n){
        For(j,0,(int)R[i].size()-1){
            cut(R[i][j]+tot);
            link(R[i][j]+tot,R[i][j]>1?R[i][j]-1+tot:1);
        }
        For(j,0,(int)L[i].size()-1){
            cut(L[i][j].fi+tot);
            link(L[i][j].fi+tot,L[i][j].se);
        }
        For(j,0,(int)Q[i].size()-1)ans[id[i][j]]=lca(Q[i][j].fi,Q[i][j].se);
    }
    For(i,1,ql)wt(ans[i]);
    flush();
    return 0;
}
View Code

 

上一篇:【汇编程序】从外设71H读取一个数M,判断其是否在10到20之间,如果M>=20,则送0FFH给外设73H;如果M<10,则送00H给外设73H;如果10<=M<20,则送88H给73H


下一篇:Nexus6 刷机/获取全局调试权限/ro.debuggable=1/root