zoukankan      html  css  js  c++  java
  • 【uoj#207】共价大爷游长沙 随机化+LCT维护子树信息

    题目描述

    给出一棵树和一个点对集合S,多次改变这棵树的形态、在集合中加入或删除点对,或询问集合内的每组点对之间的路径是否都经过某条给定边。

    输入

    输入的第一行包含一个整数 id,表示测试数据编号,如第一组数据的id=1,样例数据的 id 可以忽略。
    输入的第二行包含两个整数 n,m,分别表示图中的点数,以及接下来会发生的事件数,事件的定义下文中会有描述。初始时 S 为空。
    接下来 n−1 行,每行两个正整数 x,y,表示点 x 和点 y 之间有一条无向边。
    接下来 m 行,每行描述一个事件,每行的第一个数 type 表示事件的类型。
    若type=1,那么接下来有四个正整数x,y,u,v,表示先删除连接点x和点y的无向边,保证存在这样的无向边,然后加入一条连接点u和点v的无向边,保证操作后的图仍然满足题中所述条件。
    若type=2,那么接下来有两个正整数 x,y,表示在 S 中加入点对 (x,y)。
    若type=3,那么接下来有一个正整数 x,表示删除第 x 个加入 S 中的点对,即在第 x 个 type=2 的事件中加入 S 中的点对,保证这个点对存在且仍然在 S 中。
    若 type=4,那么接下来有两个正整数 x,y,表示小L询问守在连接点 x 和点 y 的边上是否一定能见到共价大爷,保证存在这样的无向边且此时 S 不为空。

    输出

    对于每个小L的询问,输出“YES”或者“NO”(均不含引号)表示小L一定能或者不一定能见到共价大爷。

    样例输入

    0
    5 7
    1 2
    1 3
    2 4
    1 5
    2 1 5
    1 1 5 2 5
    4 2 5
    2 1 4
    4 2 5
    3 1
    4 2 4


    题解

    随机化+LCT维护子树信息

    对与每个点对,随机一个权值,把这个权值异或到这两个点上。那么对于查询,如果 x 为树根时,y 子树中的所有点的权值的异或和等于所有点对的异或和,则视为所有点对间的路径都经过 x-y 。(别问我怎么想出来的。。。做过一道类似的题

    当权值范围足够大时可以近似视为正确。

    由于树的形态是变化的,因此需要使用LCT维护子树信息,具体方法参见这里

    注意维护子树信息的LCT:link时需要makeroot(x),makeroot(y);修改时需要makeroot(x)而不是简单的splay(x);查询时需要先makeroot(x)。

    时间复杂度 $O(LCT·nlog n)$ 

    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    int fa[N] , c[2][N] , rev[N] , w[N] , sum[N] , vx[N * 3] , vy[N * 3] , vw[N * 3] , tot;
    inline void pushup(int x)
    {
    	sum[x] = sum[c[0][x]] ^ sum[c[1][x]] ^ w[x];
    }
    inline void pushdown(int x)
    {
    	if(rev[x])
    	{
    		int l = c[0][x] , r = c[1][x];
    		swap(c[0][l] , c[1][l]) , rev[l] ^= 1;
    		swap(c[0][r] , c[1][r]) , rev[r] ^= 1;
    		rev[x] = 0;
    	}
    }
    inline bool isroot(int x)
    {
    	return c[0][fa[x]] != x && c[1][fa[x]] != x;
    }
    void update(int x)
    {
    	if(!isroot(x)) update(fa[x]);
    	pushdown(x);
    }
    inline void rotate(int x)
    {
    	int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
    	if(!isroot(y)) c[c[1][z] == y][z] = x;
    	fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
    	pushup(y) , pushup(x);
    }
    inline void splay(int x)
    {
    	int y , z;
    	update(x);
    	while(!isroot(x))
    	{
    		y = fa[x] , z = fa[y];
    		if(!isroot(y))
    		{
    			if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);
    			else rotate(y);
    		}
    		rotate(x);
    	}
    }
    inline void access(int x)
    {
    	int t = 0;
    	while(x) splay(x) , w[x] ^= sum[c[1][x]] ^ sum[t] , c[1][x] = t , t = x , x = fa[x];
    }
    inline void makeroot(int x)
    {
    	access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] ^= 1;
    }
    inline void link(int x , int y)
    {
    	makeroot(x) , makeroot(y) , fa[x] = y , w[y] ^= sum[x] , pushup(y);
    }
    inline void cut(int x , int y)
    {
    	makeroot(x) , access(y) , splay(y) , fa[x] = c[0][y] = 0 , pushup(y);
    }
    int main()
    {
    	srand(20011011);
    	int n , m , i , opt , x , y , u , v , now = 0;
    	scanf("%*d%d%d" , &n , &m);
    	for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , link(x , y);
    	while(m -- )
    	{
    		scanf("%d%d" , &opt , &x);
    		if(opt == 1) scanf("%d%d%d" , &y , &u , &v) , cut(x , y) , link(u , v);
    		else if(opt == 2)
    		{
    			scanf("%d" , &y);
    			vx[++tot] = x , vy[tot] = y , vw[tot] = (rand() << 15) + rand() , now ^= vw[tot];
    			makeroot(x) , w[x] ^= vw[tot] , pushup(x);
    			makeroot(y) , w[y] ^= vw[tot] , pushup(y);
    		}
    		else if(opt == 3)
    		{
    			now ^= vw[x];
    			makeroot(vx[x]) , w[vx[x]] ^= vw[x] , pushup(vx[x]);
    			makeroot(vy[x]) , w[vy[x]] ^= vw[x] , pushup(vy[x]);
    		}
    		else scanf("%d" , &y) , makeroot(x) , access(y) , splay(y) , puts(sum[x] == now ? "YES" : "NO");
    	}
    	return 0;
    }
    
  • 相关阅读:
    1.2 JAVA的String类和StringBuffer类
    1.7 JAVA异常总结
    2.1 JQuery框架(封装JavaScript框架)
    1.6 JSON存储数据方式(JavaScript对象表示法)
    1.33 JavaScript之HTML的DOM(三)
    1.32 JavaScript的BOM(二)
    【转】SQL 生成连续字符
    木兰国产编程语言 Mulan--附带下载地址
    【python】两行代码实现近百年的正反日期查询--20200202
    Linux下扫描服务器IP地址是否冲突(arp-scan)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8244009.html
Copyright © 2011-2022 走看看