• LCT能干啥???


    LCT能干啥

     
     
     

    模板:

    • 维护可加的树链信息:询问都是一条链上的信息;维护方式和线段树差不多;
    • 增加一条边;
    • 删除一条边;
    • 修改一个点权;
    • 修改一条路径上的所有点的点权;

     
     

    整体来说,像是树链剖分的森林化,再用(splay)代替线段树;

     
     

    (eg.) 染色

    Tree II

     
     


     
     

    维护MST

     
     

    (eg.) 水管局长

     
     

    题目要求删边,感觉删边比加边麻烦,我们可以用时光倒流法,将操作离线,沿时间轴从后往前倒着操作(撤回),会更加方便;

     
     

    因为每往(MST)(最小生成树)中加入一条边,会产生一个环,需要在这个环上删除一条最大的边才能,重新得到(MST),这是(LCT)可以做到的;

     
     

    我们遇到的的第一个问题是边权化点权,一个很方便的方法是,将每一条边都看成一个点,比如对于边((x,y)),它的编号是(i),每次连接时,都将((x,i))((i,y))相连,这样构造出来的联通性是等价的;

    边的权值被存储在(i)节点中,巧妙的解决了边权的问题;

     
     

    对于维护(MST),可以记录这颗子树里最大的边权的编号,每次增加一条边,再将路径上最大的边删去即可;

     
     


     
     

    维护双连通分量??

     
     

    (eg.) 航线规划

    感觉题目跟边双联通分量有关,将边双缩了点后,两点之间的边的数量就是我们要求的;

     
     

    那删除一条边呢?

    经验告诉我们,要时光倒流;

    那加入一条边呢?

    要重新缩点,那意思是······动态缩点???在(LCT)上缩点???

     
     

    还是从题目本身入手吧;

    先把边权化点权,用前面的方法,一个由边变来的点,才拥有权值,如果值为(1),意味着这条边是关键边,反之不是;

    (LCT)上就是维护一个子树和,询问查询路径和;

     
     

    那加入一条边呢??(又来了)

    比如加一条((x,y)),那(x)(y)就会产生一个环,那(x)(y)之间的边全都不再是关键边;那就可以打标记推平;

     
     

    至此,这个题就是路径推平路径查询;

     
     
     


     
     

    维护子树信息

     
     

    链的情况我们能解决,那子树呢?

    首先一定要明确,原树上的子树和(LCT)上的子树有巨大差别;

    (eg.) 大融合

    发现询问就是,在原树上删除((x,y))得到的子树大小的乘积;

     
     

    根据题目询问的特点,每次询问一条边((x,y)),我们可以把(x,y)这条链先单独取出来(split(x,y))

    这样我们所需要的信息就是(x,y)的虚子树大小的和;

     
     

    怎么维护虚树子树信息(记为(sz[x]))???

    我们发现虚树边只有在(access)(link)函数时会发生改变;

     
     

    (access)原函数

    void access(int x)
    { 
    	for(int y=0;x;y=x,x=fa[x])
    		splay(x),rs=y,upd(x);
    }
    

    (x)的右儿子变了,我们应当在(sz[x])减去(y)的贡献,而加上(rs)的贡献;

    贡献是什么?这里应该指的是,实儿子和虚儿子总的子树大小和,毕竟(x)虚儿子所在的整个(splay)树都在(x)子树中;

    这一部分应该为

    void upd(int x){ s[x]=s[ls]+s[rs]+1+sz[x];}
    void access(int x)
    	{
    		for(int y=0;x;x=fa[y=x])
    		{
    			splay(x),
    			sz[x]+=s[rs];
    			sz[x]-=s[y];
    			rs=y,upd(x);
    		}
    	}
    

     
     

    (link)呢?

    原函数

    void link(int x,int y)
    {
    	makert(x);fa[x]=y;
    }
    
    

    这里就是连了一条虚边;

    那应该把(x)的贡献加到(y)里;

    但由于(y)的位置未知,就必须还把(x)的贡献加给(y)的父亲们;

     
     

    为了解决这个问题,我们可以让(y)没有父亲;

    具体来说把(y) (splay)到根的位置就可以了,即

    void link(int x,int y)
    {
    	makert(x);access(y);splay(y);
    	fa[x]=y;
    	sz[y]+=s[x];
    	upd(y);
    }
    
    

     
     

    这样就可以正确维护虚子树信息。

    (Code)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    using namespace std;
    
    const int N=200005;
    int n,m;
    
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    
    struct LCT
    {
    	#define ls ch[x][0]
    	#define rs ch[x][1]
    	int fa[N],ch[N][2],tag[N],sz[N];
    	int v[N],s[N];
    	int sta[N];
    	bool nort(int x){ return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    	void upd(int x){ s[x]=s[ls]+s[rs]+1+sz[x];}
    	void rev(int x){ swap(ls,rs);tag[x]^=1;}
    	void pd(int x)
    	{
    		if(tag[x])
    		{
    			if(ls) rev(ls);
    			if(rs) rev(rs);
    			tag[x]=0;
    		}
    	}
    	void rotate(int x)
    	{
    		int y=fa[x],ys=(ch[y][1]==x);
    		int R=fa[y];
    		int B=ch[x][ys^1];
    		if(nort(y)) ch[R][ch[R][1]==y]=x; ch[x][ys^1]=y; ch[y][ys]=B;
    		if(B) fa[B]=y; fa[x]=R; fa[y]=x;
    		upd(y);upd(x);
    	}
    	void splay(int x)
    	{
    		int y=x,z,top=0;
    		sta[++top]=y;
    		while(nort(y))
    		{
    			y=fa[y];
    			sta[++top]=y;
    		}
    		while(top) pd(sta[top--]);
    		while(nort(x))
    		{
    			y=fa[x];z=fa[y];
    			if(nort(y))
    				rotate((ch[y][1]==x)==(ch[z][1]==y)?y:x);
    			rotate(x);
    		}
    	}
    	void access(int x)
    	{
    		for(int y=0;x;x=fa[y=x])
    		{
    			splay(x),
    			sz[x]+=s[rs];
    			sz[x]-=s[y];
    			rs=y,upd(x);
    		}
    	}
    	void makert(int x)
    	{
    		access(x);splay(x);rev(x);
    	}
    	void split(int x,int y)
    	{
    		makert(x);access(y);splay(y);
    	}
    	void link(int x,int y)
    	{
    		makert(x);access(y);splay(y);
    		fa[x]=y;
    		sz[y]+=s[x];
    		upd(y);
    	}
    }f;
    
    map<pair<int,int>,int> book;
    char st[5];
    
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++) f.s[i]=1;
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%s",st);
    		int x=read(),y=read();
    		if(st[0]=='A')
    		{
    			f.link(x,y);
    		}
    		else
    		{
    			f.split(x,y);
    			printf("%lld
    ",(ll)(f.sz[x]+1)*(f.sz[y]+1));
    		}
    	}
    	return 0;
    }
    

     
     
     
     

    (to be continued)

  • 相关阅读:
    简易表格练习
    CSS圆角样式
    力不从心
    学渣在努力~
    嫌疑人
    poj1308 Is it a tree?
    悲剧文本
    迷宫问题
    n皇后问题
    油田
  • 原文地址:https://www.cnblogs.com/yudes/p/LCT.html
Copyright © 2020-2023  润新知