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\n",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\n",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\n",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\n",(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\n",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\n",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\n",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;
}

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

posted @ 2018-07-04 20:45  Winniechen  阅读(629)  评论(0编辑  收藏  举报