zoukankan      html  css  js  c++  java
  • 【BZOJ4530】[Bjoi2014]大融合 LCT维护子树信息

    【BZOJ4530】[Bjoi2014]大融合

    Description

    小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
    这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
    例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
    现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

    Input

    第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
    接下来的Q行,每行是如下两种格式之一:
    A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
    Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
    1≤N,Q≤100000

    Output

    对每个查询操作,输出被查询的边的负载。

    Sample Input

    8 6
    A 2 3
    A 3 4
    A 3 8
    A 8 7
    A 6 5
    Q 3 8

    Sample Output

    6

    题解:网上看到好多并查集+线段树合并,感觉想法很好,但是还是去学了一发姜神的LCT。

    用LCT维护子树信息,维护LCT中一个点的所有儿子的信息以及所有虚儿子的信息。因为当我们对x进行access+splay操作后,原树中x的父亲全在splay中x的左子树里,并且x没有右子树,所以x的儿子都在x的虚子树中。那么如何维护虚子树的信息呢?

    发现,虚子树的信息只有在access和link时会变化,在access时,原来的右儿子变虚,又新来了一个右儿子,所以虚子树的信息要加上原来的右儿子,减去新加的右儿子。在link时,y新加了虚儿子x,那么y要加上x的信息。并且注意,为了不影响其他的点,需要对y也进行access+splay操作。至于所有实+虚儿子的信息,直接pushup就行了。

    答案就是:将y变成根,对x进行access+splay,然后ans=(y的子树信息-x的虚子树信息-1)*(x的虚子树信息+1)

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn=100010;
    struct LCT
    {
    	int rev[maxn],sx[maxn],sl[maxn],ch[maxn][2],fa[maxn];
    	bool isr(int x)	{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    	void pushup(int x)	{sl[x]=sx[x]+sl[ch[x][0]]+sl[ch[x][1]]+1;}
    	void pushdown(int x)
    	{
    		if(rev[x])
    		{
    			swap(ch[x][0],ch[x][1]);
    			if(ch[x][0])	rev[ch[x][0]]^=1;
    			if(ch[x][1])	rev[ch[x][1]]^=1;
    			rev[x]=0;
    		}
    	}
    	void updata(int x)
    	{
    		if(!isr(x))	updata(fa[x]);
    		pushdown(x);
    	}
    	void rotate(int x)
    	{
    		int y=fa[x],z=fa[y],d=(x==ch[y][1]);
    		if(!isr(y))	ch[z][y==ch[z][1]]=x;
    		fa[x]=z,fa[y]=x,ch[y][d]=ch[x][d^1];
    		if(ch[x][d^1])	fa[ch[x][d^1]]=y;
    		ch[x][d^1]=y;
    		pushup(y),pushup(x);
    	}
    	void splay(int x)
    	{
    		updata(x);
    		while(!isr(x))
    		{
    			int y=fa[x],z=fa[y];
    			if(!isr(y))
    			{
    				if((x==fa[y])^(z==fa[y]))	rotate(x);
    				else	rotate(y);
    			}
    			rotate(x);
    		}
    	}
    	void access(int x)
    	{
    		for(int y=0;x;splay(x),sx[x]+=sl[ch[x][1]],ch[x][1]=y,sx[x]-=sl[y],pushup(x),y=x,x=fa[x]);
    	}
    	void maker(int x)
    	{
    		access(x),splay(x),rev[x]^=1;
    	}
    	void link(int x,int y)
    	{
    		maker(x),access(y),splay(y);
    		sx[y]+=sl[x],fa[x]=y,pushup(y);
    	}
    }tr;
    int n,m;
    char str[5];
    int main()
    {
    	int i,a,b;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)	tr.sl[i]=1;
    	for(i=1;i<=m;i++)
    	{
    		scanf("%s%d%d",str,&a,&b);
    		if(str[0]=='A')	tr.link(a,b);
    		else
    		{
    			tr.maker(b),tr.access(a),tr.splay(a);
    			printf("%lld
    ",(long long)(tr.sl[a]-tr.sx[b]-1)*(tr.sx[b]+1));
    		}
    	}
    	return 0;
    }

     

  • 相关阅读:
    Android中内容观察者的使用---- ContentObserver类详解 (转)
    Android应用中使用及实现系统“分享”接口
    logcat的调试 比较有用的几个命令
    ASP.NET AJAX入门系列(10):Timer控件简单使用
    ASP.NET AJAX入门系列(9):在母版页中使用UpdatePanel
    ASP.NET AJAX入门系列(8):自定义异常处理
    ASP.NET AJAX入门系列(7):使用客户端脚本对UpdateProgress编程
    ASP.NET AJAX入门系列(6):UpdateProgress控件简单介绍
    ASP.NET AJAX入门系列(5):使用UpdatePanel控件(二) UpdatePanel
    ASP.NET AJAX入门系列(4):使用UpdatePanel控件(一)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7061181.html
Copyright © 2011-2022 走看看