zoukankan      html  css  js  c++  java
  • LCT

    LCT

    (写在前面的话:在学LCT的那段时间,我有点丧心病狂...前几道题的代码还可以看,后面的实在是...不忍直视...)

    LCT:用于解决动态树的一类数据结构

    大体了解:用Splay维护每个链的信息,根据一系列操作完成相应要求。

    细节掌握:

    一般操作:

    (1)Splay:LCT中的Splay和正常的Splay有一些区别,需要在Splay之前将从这个点到这颗Splay的根节点的路径提前PushDown。

    (2)rotate:没有什么大体的区别,但是需要先处理出父亲节点与子节点之间的关系。

    (3)access:不断将某个对应节点Splay到根,并且将这个节点连在虚假父节点的右子树,之后再跳到父节点继续处理一直到根节点。

    (4)makeroot:基于access操作,将这个点和根相连,将这个节点Splay到对应的根节点,之后将整个Splay翻转即可,原理:因为这个Splay维护的是每个节点深度,那么更改根节点之后,所有节点的深度排名正好掉了一个顺序。

    (5)link:LCT的重点操作,先makeroot一个,之后access+Splay另一个,之后将makeroot的那一个的父亲指向另一个。

    (6)cut:LCT的重点操作,先makeroot一个,之后access+Splay另一个,可以发现,第一个是另一个的左儿子,之后直接将两个Splay断开即可。

    (7)find:找到所在的树是哪一个,先access,之后Splay,之后再找到深度最小的(根节点),通过不停向左子树找实现。

    说实话,LCT是真的好写...但是如果发现你需要调的时候,删了重写吧!

    例题时间:

    BZOJ2049: [Sdoi2008]Cave 洞穴勘测

    分析:LCT练习题,动态维护两个节点的连通性。每次Destory就是cut,Connect就是link,Query就是find两次判断是否相等。

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    #define N 10050
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[N][2],rev[N],f[N],n,Q;
    char s[15];
    void PushDown(int rt)
    {
        if(rev[rt])
        {
            swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
            rev[ls]^=1,rev[rs]^=1;rev[rt]=0;
        }
    }
    void Update(int rt)
    {
        if(!isroot(rt))Update(f[rt]);
        PushDown(rt);
    }
    void rotate(int x)
    {
        int y=f[x],z=f[y],k=get(x);
        if(!isroot(y))ch[z][ch[z][1]==y]=x;
        ch[y][k]=ch[x][!k];f[ch[y][k]]=y;
        ch[x][!k]=y;f[y]=x;f[x]=z;
    }
    void Splay(int rt)
    {
        //puts("a");
        Update(rt);
        //puts("b");
        for(int fa;!isroot(rt);rotate(rt))
        {
            fa=f[rt];
            if(!isroot(fa))rotate(get(rt)==get(fa)?fa:rt);
        }
    }
    void access(int rt)
    {
        int t=0;
        while(rt){Splay(rt);rs=t;t=rt;rt=f[rt];}
    }
    void make_root(int rt)
    {
        access(rt);Splay(rt);
        swap(ls,rs);rev[rt]^=1;
    }
    void link(int x,int rt)
    {
        make_root(x);f[x]=rt;
    }
    void cut(int x,int rt)
    {
        make_root(x);access(rt);Splay(rt);ls=f[x]=0;
    }
    int find(int rt)
    {
        access(rt);Splay(rt);
        while(ls)PushDown(rt),rt=ls;
        return rt;
    }
    int main()
    {
        scanf("%d%d",&n,&Q);
        while(Q--)
        {
            int x,y;
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='C')link(x,y);
            else if(s[0]=='D')cut(x,y);
            else
            {
                x=find(x);y=find(y);
                if(x==y)puts("Yes");
                else puts("No");
            }
        }
        return 0;
    }

    BZOJ3282: Tree

    分析:LCT练习题,维护链上点权异或和

    为了方便操作,建议在修改的时候先Splay到根,之后在操作,不然挺麻烦的...另外,记得在link和cut之前判断是否联通。至于点权异或和怎么维护,就是类似Splay维护区间操作的方式,大体没有什么不同。

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    #define N 300050
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[N][2],f[N],sum[N],n,m,val[N],rev[N];
    void PushDown(int rt)
    {
    	if(rev[rt])
    	{
    		swap(ch[rs][1],ch[rs][0]);swap(ch[ls][0],ch[ls][1]);
    		rev[rs]^=1,rev[ls]^=1,rev[rt]=0;
    	}
    }
    void PushUp(int rt){sum[rt]=val[rt]^sum[ls]^sum[rs];}
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
    	int x=f[rt],y=f[x],k=get(rt);
    	if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
    	ch[x][k]=ch[rt][!k];f[ch[rt][!k]]=x;ch[rt][!k]=x;f[x]=rt;f[rt]=y;
    	PushUp(x);PushUp(rt);
    }
    void Splay(int rt)
    {
    	int fa;
    	for(Update(rt);!isroot(rt);rotate(rt))
    		if(!isroot(f[rt]))fa=f[rt],rotate(get(fa)==get(rt)?fa:rt);
    }
    void access(int rt)
    {
    	int t=0;
    	while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];
    }
    void make_root(int rt)
    {
    	access(rt);Splay(rt);
    	swap(ls,rs);rev[rt]^=1;
    }
    void link(int x,int rt){make_root(x);f[x]=rt;}
    void cut(int x,int rt){make_root(x);access(rt);Splay(rt);f[x]=ls=0;}
    int find(int rt)
    {
    	access(rt);Splay(rt);
    	while(ls)PushDown(rt),rt=ls;
    	return rt;
    }
    void fix(int x,int v){Splay(x);sum[x]^=val[x];val[x]=v;sum[x]^=v;}
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    	while(m--)
    	{
    		int op,x,y;
    		scanf("%d%d%d",&op,&x,&y);
    		if(!op)
    		{
    			make_root(x);access(y);Splay(y);
    			printf("%d
    ",sum[y]);
    		}else if(op==1)
    		{
    			int fx=find(x),fy=find(y);
    			if(fx!=fy)link(x,y);
    		}else if(op==2)
    		{
    			int fx=find(x),fy=find(y);
    			if(fx==fy)cut(x,y);
    		} 
    		else fix(x,y);
    	}
    	return 0;
    }

    BZOJ2631: tree

    分析:LCT练习题,比上一道题多了一个区间加和区间乘法。

    修改的时候先makeroot一个,再access+Splay另一个,之后直接修改,记得打上lazy标记。

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    #define N 100055
    #define mod 51061
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[N][2],f[N],sum[N],add[N],mul[N],rev[N],val[N],n,Q,siz[N];
    void PushUp(int rt)
    {
        siz[rt]=siz[ls]+siz[rs]+1;
        sum[rt]=(sum[ls]+sum[rs]+val[rt])%mod;
    }
    void PushDown(int rt)
    {
        if(mul[rt]!=1)
        {
            val[ls]=(1ll*val[ls]*mul[rt])%mod;sum[ls]=(1ll*sum[ls]*mul[rt])%mod;mul[ls]=(1ll*mul[ls]*mul[rt])%mod;add[ls]=(1ll*add[ls]*mul[rt])%mod;
            val[rs]=(1ll*val[rs]*mul[rt])%mod;sum[rs]=(1ll*sum[rs]*mul[rt])%mod;mul[rs]=(1ll*mul[rs]*mul[rt])%mod;add[rs]=(1ll*add[rs]*mul[rt])%mod;
            mul[rt]=1;
        }
        if(add[rt])
        {
            val[ls]=(add[rt]+val[ls])%mod;sum[ls]=((1ll*add[rt]*siz[ls])%mod+sum[ls])%mod;add[ls]=(add[rt]+add[ls])%mod;
            val[rs]=(add[rt]+val[rs])%mod;sum[rs]=((1ll*add[rt]*siz[rs])%mod+sum[rs])%mod;add[rs]=(add[rt]+add[rs])%mod;
            add[rt]=0;
        }
        if(rev[rt])
        {
            swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
            rev[rs]^=1,rev[ls]^=1,rev[rt]=0;
        }
    }
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
        int x=f[rt],y=f[x],k=get(rt);
        if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
        ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
        ch[rt][!k]=x;f[x]=rt;f[rt]=y;
        PushUp(x);PushUp(rt);
    }
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
    void access(int rt){int t=0;while(rt){Splay(rt);rs=t;PushUp(rt);t=rt;rt=f[rt];}}
    void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
    void link(int x,int rt){make_root(x);f[x]=rt;}
    void cut(int x,int rt){make_root(x);access(rt);Splay(rt);f[x]=ls=0;}
    void splite(int x,int rt){make_root(x);access(rt);Splay(rt);}
    void Update_mul(int x,int rt,int c)
    {
        splite(x,rt);
        mul[rt]=(1ll*mul[rt]*c)%mod;add[rt]=(1ll*add[rt]*c)%mod;
        sum[rt]=(1ll*sum[rt]*c)%mod;val[rt]=(1ll*val[rt]*c)%mod;
    }
    void Update_add(int x,int rt,int c)
    {
        splite(x,rt);
        sum[rt]=(sum[rt]+(1ll*siz[rt]*c)%mod)%mod;
        val[rt]=(c+val[rt])%mod;add[rt]=(c+add[rt])%mod;
    }
    char s[10];
    int main()
    {
        scanf("%d%d",&n,&Q);
        for(int i=1;i<=n;i++)val[i]=mul[i]=siz[i]=sum[i]=1;
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);link(x,y);
        }
        while(Q--)
        {
            int x,y,z,k;
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='*')
            {
                scanf("%d",&z);
                Update_mul(x,y,z);
            }else if(s[0]=='-')
            {
                scanf("%d%d",&z,&k);
                cut(x,y);
                link(z,k);
            }else if(s[0]=='+')
            {
                scanf("%d",&z);
                Update_add(x,y,z);
            }else
            {
                splite(x,y);
                printf("%d
    ",sum[y]);
            }
        }
        return 0;
    }

    BZOJ1180: [CROATIAN2009]OTOCI

    分析:LCT练习题,维护连通性以及链上点权和

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    #define N 30050
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int sum[N],rev[N],val[N],ch[N][2],f[N],n,Q;
    char s[N];
    void PushUp(int rt)
    {
        sum[rt]=sum[ls]+sum[rs]+val[rt];
    }
    void PushDown(int rt)
    {
        if(rev[rt])
        {
            swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
            rev[ls]^=1,rev[rs]^=1,rev[rt]=0;
        }
    }
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
        int x=f[rt],y=f[x],k=get(rt);
        if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
        ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
        ch[rt][!k]=x;f[x]=rt;f[rt]=y;
        PushUp(x);PushUp(rt);
    }
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
    void access(int rt){int t=0;while(rt){Splay(rt);rs=t;PushUp(rt);t=rt;rt=f[rt];}}
    void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
    void link(int x,int rt){make_root(x);f[x]=rt;}
    int find(int rt)
    {
        access(rt);Splay(rt);
        while(ls)PushDown(rt),rt=ls;
        return rt;
    }
    void fix(int rt,int v){access(rt);Splay(rt);val[rt]=v;PushUp(rt);}
    void splite(int x,int rt){make_root(x);access(rt);Splay(rt);}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        scanf("%d",&Q);
        while(Q--)
        {
            int x,y;
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='e')
            {
                if(find(x)!=find(y))puts("impossible");
                else
                {
                    splite(x,y);
                    printf("%d
    ",sum[y]);
                }
            }else if(s[0]=='b')
            {
                if(find(x)==find(y))puts("no");
                else
                {
                    puts("yes");link(x,y);
                }
            }else fix(x,y);
        }
        return 0;
    }

    BZOJ3669: [Noi2014]魔法森林

    分析:LCT一种套路,动态维护树,贪心的将最坏的替代,之后链上最大值。

    先按照a排序,做kruscal,之后动态将b最大的替换,更新答案即可。LCT只能维护点权,新建一个节点当做边吧...

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    #define N 150050
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][1]==rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[N][2],f[N],rev[N],n,m,val[N],maxx[N];
    struct node
    {
        int x,y,a,b;
    }a[N];
    bool cmp(const node &a,const node &b)
    {
        return a.a<b.a;
    }
    void PushUp(int rt)
    {
        maxx[rt]=rt;
        if(val[maxx[ls]]>val[maxx[rt]])maxx[rt]=maxx[ls];
        if(val[maxx[rs]]>val[maxx[rt]])maxx[rt]=maxx[rs];
    }
    void PushDown(int rt)
    {
        if(rev[rt])
        {
            rev[ls]^=1,rev[rs]^=1,rev[rt]=0;
            swap(ch[ls][0],ch[ls][1]);swap(ch[rs][0],ch[rs][1]);
        }
    }
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
        int x=f[rt],y=f[x],k=get(rt);
        if(!isroot(x))ch[y][get(x)]=rt;
        ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
        ch[rt][!k]=x;f[x]=rt;f[rt]=y;
        PushUp(x);PushUp(rt);
    }
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
    void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
    void make_root(int rt){access(rt);Splay(rt);swap(ls,rs);rev[rt]^=1;}
    void link(int x,int rt){make_root(x);Splay(rt);f[x]=rt;}
    void cut(int x,int rt){make_root(x);access(rt);Splay(rt);ls=f[x]=0;}
    int find(int rt){access(rt);Splay(rt);while(ls)PushDown(rt),rt=ls;return rt;}
    int query(int x,int rt){make_root(x);access(rt);Splay(rt);return maxx[rt];}
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].a,&a[i].b);}
        sort(a+1,a+m+1,cmp);
        int ans=1<<30;
        for(int i=1;i<=m;i++)
        {
            int x=a[i].x,y=a[i].y,v1=a[i].a,v2=a[i].b;
            int fx=find(x),fy=find(y);
            int tot=i+n;
            if(fx!=fy)
            {
                val[tot]=v2;maxx[tot]=tot;link(x,tot);link(tot,y);
            }else
            {
                int tmp=query(x,y);
                if(val[tmp]>v2)
                {
                    cut(a[tmp-n].x,tmp);cut(a[tmp-n].y,tmp);val[tot]=v2;maxx[tot]=tot;
                    link(x,tot);link(y,tot);
                }
            }
            if(find(1)==find(n))
            {
                ans=min(ans,v1+val[query(1,n)]);
            }
        }
        printf("%d
    ",(ans==1<<30)?-1:ans);
        return 0;
    }

    BZOJ4530: [Bjoi2014]大融合

    分析:LCT维护子树信息。

    其实本质上还是维护一个链上信息,只是这个链上的点的信息包括了所有点的子节点的信息,也就是说,将这个树的信息维护出来,原理就是在每次access的时候,将你舍去的部分(每个点的rson)同样记录到答案之中。

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    #define N 100505
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][1]==rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int f[N],ch[N][2],siz[N],rev[N],all[N],n,Q;char s[10];
    void PushDown(int rt){if(rev[rt])swap(ch[ls][0],ch[ls][1]),swap(ch[rs][0],ch[rs][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
    void PushUp(int rt){all[rt]=all[ls]+all[rs]+siz[rt]+1;}
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
    	int x=f[rt],y=f[x],k=get(rt);
    	if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
    	ch[x][k]=ch[rt][!k],f[ch[x][k]]=x;
    	ch[rt][!k]=x;f[x]=rt,f[rt]=y;
    	PushUp(x),PushUp(rt);
    }
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate(get(f[rt])==get(rt)?f[rt]:rt);}
    void access(int rt){int t=0;while(rt)Splay(rt),siz[rt]+=all[rs]-all[t],rs=t,PushUp(rt),t=rt,rt=f[rt];}
    void make_root(int rt){access(rt),Splay(rt);swap(ls,rs),rev[rt]^=1;}
    void link(int x,int rt){make_root(x);make_root(rt);f[x]=rt;siz[rt]+=all[x];PushUp(rt);}
    int main()
    {
    	scanf("%d%d",&n,&Q);
    	for(int i=1;i<=n;i++)all[i]=1;
    	while(Q--)
    	{
    		int x,y;
    		scanf("%s%d%d",s,&x,&y);
    		if(s[0]=='A')link(x,y);
    		else
    		{
    			make_root(x);make_root(y);
    			printf("%lld
    ",1ll*all[x]*(all[y]-all[x]));
    		}
    	}
    	return 0;
    }

    BZOJ2594: [Wc2006]水管局长数据加强版

    吐槽:好端端的题为什么要加强数据,据说不能直接link,所以写了kruscal,据说不能用map,所以重载了小于号,之后用lower_bound解决,(我*****)

    分析:LCT维护动态树,Splay维护链上最大值。

    删边还是离线下来往里插吧...之后类似魔法森林,然而删边不告诉编号...二分找吧...

    数据范围那么大...LCT的常数感人...所以Kruscal吧...

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <queue>
    #include <iostream>
    using namespace std;
    inline char nc()
    {
        static char buf[100000],*p1,*p2;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int rd()
    {
        register int x=0;register char s=nc();
        while(s<'0'||s>'9')s=nc();
        while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+s-'0',s=nc();
        return x;
    }
    #define N 100005
    #define M 1100005
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[M][2],f[M],val[M],rev[M],mx[M],cnt,n,m,Q,fa[N],killx[M],killy[M],ans[N];
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    struct node{int x,y,v,flag;
    	friend bool operator<(const node &a,const node &b){return ((a.x==b.x)&(b.y==a.y)&(a.v<b.v))|((a.x==b.x)&(a.y<b.y))|(a.x<b.x);}}e[M],ev[M];
    bool cmp(const node &a,const node &b){return a.v<b.v;}
    struct QAQ{int op,x,y,pos;}q[N];
    void PushUp(int rt){mx[rt]=rt;if(val[mx[ls]]>val[mx[rt]])mx[rt]=mx[ls];if(val[mx[rs]]>val[mx[rt]])mx[rt]=mx[rs];}
    void PushDown(int rt){if(rev[rt])swap(ch[rs][0],ch[rs][1]),swap(ch[ls][0],ch[ls][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void rotate(int rt)
    {
    	int x=f[rt],y=f[x],k=get(rt);
    	if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
    	ch[x][k]=ch[rt][!k];f[ch[x][k]]=x;
    	ch[rt][!k]=x;f[x]=rt;f[rt]=y;
    	PushUp(x);PushUp(rt);
    }
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate((get(rt)==get(f[rt]))?f[rt]:rt);}
    void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
    void make_root(int rt){access(rt);Splay(rt);rev[rt]^=1;swap(ls,rs);}
    void link(int x,int rt){make_root(x);Splay(rt);f[x]=rt;}
    void cut(int x,int rt){make_root(x);access(rt);Splay(rt);ls=f[x]=0;}
    int query(int x,int rt){make_root(x);access(rt);Splay(rt);return mx[rt];}
    int main()
    {
    	n=rd();m=rd();Q=rd();
    	for(int i=1;i<=m;i++){e[i].x=rd();e[i].y=rd();e[i].v=rd();if(e[i].x>e[i].y)swap(e[i].x,e[i].y);}
    	sort(e+1,e+m+1);for(int i=1;i<=n;i++)fa[i]=i;
    	for(int i=1;i<=m;i++)ev[i]=e[i];
    	for(int i=1;i<=Q;i++)
    	{
    		q[i].op=rd();q[i].x=rd();q[i].y=rd();
    		if(q[i].op==1)continue;
    		if(q[i].x>q[i].y)swap(q[i].x,q[i].y);
    		q[i].pos=lower_bound(e+1,e+m+1,node{q[i].x,q[i].y,0,0})-e;
    		e[q[i].pos].flag=ev[q[i].pos].flag=1;
    	}
    	sort(ev+1,ev+1+m,cmp);int tot=n,cnt=0;
    	for(int i=1;i<=m;i++)
    	{
    		if(!ev[i].flag)
    		{
    			int x=ev[i].x,y=ev[i].y;
    			if(find(x)!=find(y))val[++tot]=ev[i].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,link(x,tot),link(tot,y),fa[find(x)]=find(y);
    		}
    	}
    	while(Q--)
    	{
    		int x=q[Q+1].x,y=q[Q+1].y;
    		if(q[Q+1].op==1)ans[++cnt]=val[query(x,y)];
    		else
    		{
    			int tmp=q[Q+1].pos;
    			if(find(x)!=find(y))val[++tot]=e[tmp].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,link(x,tot),link(tot,y),fa[find(x)]=find(y);
    			else
    			{
    				int t=query(x,y);
    				if(val[t]>e[tmp].v)val[++tot]=e[tmp].v,killx[tot]=x,killy[tot]=y,mx[tot]=tot,cut(killx[t],t),cut(t,killy[t]),link(x,tot),link(tot,y);
    			}
    		}
    	}
    	while(cnt--)printf("%d
    ",ans[cnt+1]);
    }

    BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

    分析:转化一下变成LCT的模型,n+1个点n条边的动态树,上面都做这么多题了,大概也明白了吧...

    至于查询的时候,makeroot(n+1),之后access(x)+Splay(x),可以发现,根的左子树大小即为答案,原因是答案就是n+1到x直接那条链的点数。

    附上代码:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <queue>
    using namespace std;
    #define N 200005
    #define ls ch[rt][0]
    #define rs ch[rt][1]
    #define get(rt) (ch[f[rt]][0]!=rt)
    #define isroot(rt) (ch[f[rt]][0]!=rt&&ch[f[rt]][1]!=rt)
    int ch[N][2],f[N],rev[N],siz[N],a[N],n,Q;
    void PushUp(int rt){siz[rt]=siz[ls]+siz[rs]+1;}
    void rotate(int rt)
    {
    	int x=f[rt],y=f[x],k=get(rt);
    	if(!isroot(x))ch[y][ch[y][0]!=x]=rt;
    	ch[x][k]=ch[rt][!k],f[ch[x][k]]=x;
    	ch[rt][!k]=x,f[x]=rt,f[rt]=y;PushUp(x),PushUp(rt);
    }
    void PushDown(int rt){if(rev[rt])swap(ch[ls][0],ch[ls][1]),swap(ch[rs][0],ch[rs][1]),rev[ls]^=1,rev[rs]^=1,rev[rt]=0;}
    void Update(int rt){if(!isroot(rt))Update(f[rt]);PushDown(rt);}
    void Splay(int rt){for(Update(rt);!isroot(rt);rotate(rt))if(!isroot(f[rt]))rotate(get(rt)==get(f[rt])?f[rt]:rt);}
    void access(int rt){int t=0;while(rt)Splay(rt),rs=t,PushUp(rt),t=rt,rt=f[rt];}
    void makeroot(int rt){access(rt);Splay(rt);rev[rt]^=1;swap(ls,rs);}
    void link(int rt,int x){makeroot(x);Splay(rt);f[x]=rt;}
    void cut(int rt,int x){makeroot(x);access(rt);Splay(rt);ls=f[x]=0;}
    int query(int rt){makeroot(n+1);access(rt);Splay(rt);return siz[ls];}
    int main()
    {
    	scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]),link(i,min(i+a[i],n+1));scanf("%d",&Q);
    	while(Q--)
    	{
    		int op,x,y;
    		scanf("%d%d",&op,&x);x++;
    		if(op==1)printf("%d
    ",query(x));
    		else scanf("%d",&y),cut(x,min(x+a[x],n+1)),link(x,min(y+x,n+1)),a[x]=y;
    	}return 0;
    }

    还有什么温暖会指引我们前行之类的,但是我还没有做...就先更新到这里吧...

  • 相关阅读:
    【SHOI2002】百事世界杯之旅
    【LGOJ 3384】树链剖分
    [20191006机房测试] 括号序列
    [20191006机房测试] 矿石
    【SHOI2012】回家的路
    [20191005机房测试] Seat
    [20191005机房测试] Silhouette
    每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样
    fgets函数读取最后一行的时候为什么会重复
    c语言中返回的变量地址,其物理地址在?(刨根问底)
  • 原文地址:https://www.cnblogs.com/Winniechen/p/9265227.html
Copyright © 2011-2022 走看看