zoukankan      html  css  js  c++  java
  • LCT(2)

    LCT(2)

    关于 LCT 的基本操作和代码实现见 (1) 。

    5. LCT的应用

    5.0 LCT 裸题

    就是LCT的基本操作模板题,常出现于早年省选。不讨论。

    5.1 LCT维护子树信息

    很多时候,我们需要用 LCT 来维护子树的信息。

    5.1.1 例1:[BJOI2014] 大融合

    这题要求我们支持动态维护子树大小。我们对于每个点分别维护实子树和和虚子树和即可。

    具体见代码。没注释的都是板子。

    #include<cstdio>
    inline int gi()
    {
    	char c; int x=0;
    	for(;c<'0'||c>'9';c=getchar());
    	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    	return x;
    }
    const int N=100005;
    int ch[N][2],fa[N],n,m,w[N],st[N],size[N],isze[N];
    bool rev[N];
    #define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
    #define swap(x,y) (x^=y^=x^=y)
    void pushup(int x) {
    	size[x]=size[ch[x][0]]+size[ch[x][1]]+isze[x]+1; //注意加上虚子树大小
    }
    void pushdown(int x)
    {
    	if(rev[x])
    	{
    		rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
    		swap(ch[x][0],ch[x][1]);
    		rev[x]=0;
    	}
    }
    void rotate(int x){
    	int y=fa[x],z=fa[y];
    	bool k=ch[y][0]==x; int w=ch[x][k];
    	if(isnrt(y))ch[z][ch[z][1]==y]=x;ch[x][k]=y;ch[y][!k]=w;
    	fa[w]=y;fa[y]=x;fa[x]=z;
    	pushup(y); pushup(x);
    }
    void splay(int x)
    {
    	int y=x,tp=1; st[1]=y;
    	while(isnrt(y)) st[++tp]=y=fa[y];
    	while(tp) pushdown(st[tp--]);
    	while(isnrt(x))
    	{
    		int y=fa[x],z=fa[y];
    		if(isnrt(y)) (ch[z][1]==y)^(ch[y][1]==x)?rotate(x):rotate(y);
    		rotate(x);
    	}
    }
    void access(int x)
    {
    	int y=0;
    	for(;x;y=x,x=fa[x])
    	{
    		splay(x);isze[x]+=size[ch[x][1]]; //更新x虚儿子信息
    		ch[x][1]=y,isze[x]-=size[y];
    		pushup(x);
    	}
    }
    void makert(int x) {
    	access(x),splay(x),rev[x]^=1;
    }
    void split(int x, int y) {
    	makert(x),access(y),splay(y);
    }
    void link(int x, int y) {
    	split(x,y),fa[x]=y; //注意这里是split,要将y置为辅助树的根
    	isze[y]+=size[x],pushup(y); //x为y的虚儿子
    }
    int main()
    {
    	n=gi(),m=gi();
    	while(m--)
    	{
    		char op[2]; scanf("%s",op);
    		int x=gi(),y=gi();
    		if(op[0]=='A') link(x,y);
    		else {
    			split(x,y);
    			printf("%lld
    ",1ll*size[x]*(size[y]-size[x]));
    		}
    	}
    }
    
    

    本部分例题待补充。

    5.2 LCT维护边权信息

    LCT 的板子维护的都是点权。怎么维护边权呢?

    其实很简单。连边的时候只要新建一个节点,点权为两点边权,然后与两点连边即可。

    暂无例题。

    5.3 LCT维护最小生成树

    这个是 LCT 比较常见的应用:支持动态维护最小生成树(森林)。

    按照上面的方法维护边权最大值。

    加入一条边后判断有没有成环(用并查集维护),如果成环就把边权最大的边给砍掉。

    5.3.1 例1 道路重建 (OJ1901)

    给定一个图,每次询问只有第 (L) 条边到第 (R) 条边可用, (A) 点到 (B) 点是否连通。

    Sol: 离线处理。按顺序加边维护最小生成森林,每条边的边权为该边的编号。处理到每个第 (R) 条边时,只需要查询 (A ightarrow B) 的最小边权是否 (>=L) 即可。

    下为代码,省略了板子部分

    int fa[N],ch[N][2],st[N],val[N],mn[N],ind,f[N],pa[N],pb[N]; bool rev[N];
    int findset(int x)
    {
    	if(f[x]==x) return x;
    	return f[x]=findset(f[x]);
    }
    #define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
    void pushup(int x)
    {
    	mn[x]=x;
    	if(val[mn[ch[x][0]]]<val[mn[x]]) mn[x]=mn[ch[x][0]];
    	if(val[mn[ch[x][1]]]<val[mn[x]]) mn[x]=mn[ch[x][1]];
    }
    //此处省略部分板子内容
    int query(int x, int y) { //查询x->y的最小边权
    	split(x,y); return mn[y];
    }
    void link3(int x, int y, int w)
    {
    	if(x==y) return ;
    	int fx=findset(x),fy=findset(y);
    	if(fx==fy)
    	{
    		int t=query(x,y);
    		cut(t,pa[t]),cut(t,pb[t]); //若成环,删除最小的边
    	}
    	else f[fx]=fy;
    	val[++ind]=w;link(x,ind),link(y,ind); //新建虚拟节点,连边
    	pa[ind]=x,pb[ind]=y;
    }
    int n,m,qn,u[N],v[N],l[N],r[N],a[N],b[N];
    bool ans[N];
    vector<int> ve[N];
    int main()
    {
    	memset(val,0x3f,sizeof(val));
    	ind=n=gi(),m=gi();
    	for(int i=0;i<=n;i++) mn[i]=f[i]=i;
    	for(int i=1;i<=m;i++) u[i]=gi(),v[i]=gi();
    	qn=gi();
    	for(int i=1;i<=qn;i++)
    		l[i]=gi(),r[i]=gi(),a[i]=gi(),b[i]=gi(),ve[r[i]].push_back(i);
    	for(int i=1;i<=m;i++)
    	{
    		link3(u[i],v[i],i);
    		for(int j=0;j<ve[i].size();j++)
    		{
    			int k=ve[i][j];
    			if(findset(a[k])!=findset(b[k])) continue;
    			int x=query(a[k],b[k]);
    			ans[k]=(l[k]<=val[x]);
    		}
    	}
    	for(int i=1;i<=qn;i++) putchar(ans[i]+'0'),putchar('
    ');
    }
    

    5.3.2 例2 [2016清华集训]温暖会指引我们前行

    题意晦涩难懂,应该是动态修改边权,两点间最大生成树上的路径。

    总之就是LCT动态维护最大生成树。

    //删去开头和LCT板子内容
    int ch[N][2],fa[N],f[N],st[N],tem[N],len[N],mn[N],sum[N],pa[N],pb[N],ind,n,m;
    bool rev[N];
    #define lc ch[x][0]
    #define rc ch[x][1]
    #define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
    #define swap(x,y) (x^=y^=x^=y)
    int findset(int x) {
    	if(f[x]==x) return x;
    	return f[x]=findset(f[x]);
    }
    void pushup(int x)
    {
    	mn[x]=x;
    	if(tem[mn[lc]]<tem[mn[x]]) mn[x]=mn[lc];
    	if(tem[mn[rc]]<tem[mn[x]]) mn[x]=mn[rc];
    	sum[x]=sum[lc]+sum[rc]+len[x];
    }
    void work(int id, int x, int y, int t, int l)
    {
    	int fx=findset(x),fy=findset(y);
    	if(fx==fy)
    	{
    		split(x,y);
    		int mt=mn[y];
    		if(tem[mt]>t) return ;
    		cut(mt,pa[mt]),cut(mt,pb[mt]);
    	}
    	else f[fx]=fy;
    	len[n+id]=l,tem[n+id]=t,link(x,n+id),link(y,n+id);
    	pa[n+id]=x,pb[n+id]=y;
    }
    int main()
    {
    	n=gi(),m=gi();
    	memset(tem,0x3f,sizeof(tem));
    	for(int i=1;i<=n;i++) mn[i]=f[i]=i;
    	while(m--)
    	{
    		char op[10]; scanf("%s",op);
    		if(op[0]=='f')
    		{
    			int id=gi()+1,u=gi()+1,v=gi()+1,t=gi(),l=gi();
    			work(id,u,v,t,l);
    		}
    		if(op[0]=='m')
    		{
    			int u=gi()+1,v=gi()+1;
    			if(findrt(u)!=findrt(v)) {
    				puts("-1");
    				continue;
    			}
    			split(u,v);
    			printf("%d
    ",sum[v]);
    		}
    		if(op[0]=='c')
    		{
    			int id=gi()+1,l=gi();
    			splay(n+id);
    			len[n+id]=l;
    			pushup(n+id);
    		}
    	}
    }
    

    5.3.3 [JSTSC2015]最小生成树

    题意:给一个无向图,每次询问仅保留权值在 (lsim r) 的边,形成的最小生成森林的权值和为多少。

    Sol (By wyj): LCT+主席树。用LCT维护最小生成树,用主席树维护加入前 (i) 条边时,图中最小生成树的情况(边权降序排序)。LCT上加删第 (k) 条边,就在主席树上位置 (k) 加减权值。对于每个询问二分得到位置区间,直接查询主席树上该区间的权值和即可。

    //删去开头和LCT板子内容
    int ch[N][2],fa[N],st[N],f[N],pa[N],pb[N];
    int rt[N*80],sum[N*80],ls[N*80],rs[N*80],val[N],mx[N],w[N];
    int n,m,q,b,ind,lst,nn,cnt;
    bool rev[N];
    unordered_map<int,int> mp;
    int update(int& x, int l, int r, int s, int w)
    {
    	int x2=++cnt;
    	ls[x2]=ls[x],rs[x2]=rs[x],sum[x2]=sum[x]+w;
    	if(l==r) return x2;
    	int mid=l+r>>1;
    	if(s<=mid) ls[x2]=update(ls[x],l,mid,s,w);
    	else rs[x2]=update(rs[x],mid+1,r,s,w);
    	return x2;
    }
    int query(int x, int l, int r, int sl, int sr)
    {
    	if(sl<=l&&r<=sr) return sum[x];
    	if(sl>r||sr<l) return 0;
    	int mid=l+r>>1;
    	return query(ls[x],l,mid,sl,sr)+query(rs[x],mid+1,r,sl,sr);
    }
    struct edge {
    	int u,v,w;
    	bool operator < (edge x) const {
    		return w<x.w;
    	}
    } e[N];
    int findset(int x) {
    	if(f[x]==x) return x;
    	return f[x]=findset(f[x]);
    }
    #define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
    void pushup(int x)
    {
    	mx[x]=x;
    	if(val[mx[ch[x][0]]]>val[mx[x]]) mx[x]=mx[ch[x][0]];
    	if(val[mx[ch[x][1]]]>val[mx[x]]) mx[x]=mx[ch[x][1]];
    }
    int findmx(int x, int y) {
    	split(x,y); return mx[y];
    }
    void link(int id, int x, int y, int w)
    {
    	if(x==y) return ;
    	int fx=findset(x),fy=findset(y);
    	if(fx==fy)
    	{
    		int t=findmx(x,y);
    		cut(t,pa[t]),cut(t,pb[t]);
    		rt[id]=update(rt[id],1,nn,mp[val[t]],-val[t]);
    	}
    	else f[fx]=fy;
    	rt[id]=update(rt[id],1,nn,mp[w],w);
    	val[++ind]=w;link(x,ind),link(y,ind);
    	pa[ind]=x,pb[ind]=y;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("1517.in","r",stdin);
    #endif
    	memset(val,-0x3f,sizeof(val));
    	ind=n=gi(),m=gi();
    	for(int i=0;i<=n;i++) f[i]=mx[i]=i;
    	for(int i=1;i<=m;i++) e[i].u=gi(),e[i].v=gi(),w[i]=e[i].w=gi();
    	sort(e+1,e+1+m);
    	for(int i=1;i<=m;i++)
    	{
    		if(e[i].w!=e[i-1].w) ++nn;
    		mp[e[i].w]=nn;
    	}
    	for(int i=m;i;i--)
    	{
    		rt[i]=rt[i+1];
    		link(i,e[i].u,e[i].v,e[i].w);
    	}
    	q=gi(),b=gi();
    	sort(w+1,w+1+m); int mm=unique(w+1,w+1+m)-w-1;
    	while(q--)
    	{
    		int l=gi()+lst*b,r=gi()+l;
    		int p=lower_bound(e+1,e+1+m,(edge){0,0,l})-e;
    		l=lower_bound(w+1,w+1+mm,l)-w;
    		r=upper_bound(w+1,w+1+mm,r)-w-1;
    		printf("%d
    ",lst=query(rt[p],1,nn,l,r));
    	}
    }
    

    5.4 利用 access 复杂度的数据结构题

    经典例题:「SDOI2017」树点涂色

    (题解鸽了鸽了)

    5.5 LCT的其他题目

    LCT还有其他一些有趣的题目。以及一些综合题杂题经常涉及LCT!

    待补充

  • 相关阅读:
    简明Python3教程 12.问题解决
    简明Python3教程 11.数据结构
    【SPOJ 694】Distinct Substrings
    【codeforces Manthan, Codefest 17 C】Helga Hufflepuff's Cup
    【CF Manthan, Codefest 17 B】Marvolo Gaunt's Ring
    【CF Manthan, Codefest 17 A】Tom Riddle's Diary
    【SPOJ 220】 PHRASES
    【POJ 3261】Milk Patterns
    【POJ 3294】Life Forms
    【POJ 1226】Substrings
  • 原文地址:https://www.cnblogs.com/farway17/p/10425532.html
Copyright © 2011-2022 走看看