zoukankan      html  css  js  c++  java
  • UOJ207 共价大爷游长沙

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

    题目链接:UOJ207

    正解:$LCT$+其他

    解题报告:

      这道题的最巧妙的一步转化就是给每个行动随机一个权值,对于每个询问,如果把$x$旋到根,同时把$y$也$access$之后,那么$y$的子树异或和就是经过了$x$到$y$这条边的行动的异或和。

      如果为全局异或值则说明全部都经过了。

      考虑如何用$LCT$维护子树信息,我们分别维护整棵子树和只考虑虚儿子上的信息。

      那么只需要在轻重边切换(就是$access$和$link$的时候)的时候$update$就好了,因为是异或信息,可以直接通过再异或一次修改,很方便。

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <cstdio>
    #include <string>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #define lc root<<1
    #define rc root<<1|1
    #define rep(i,j,k) for(int i=j;i<=k;i++)
    #define reg(i,x) for(int i=first[x];i;i=next[i])
    using namespace std;
    typedef long long LL;
    const int MAXN = 300011;
    int ID,n,m,father[MAXN],tag[MAXN],tr[MAXN][2],top,stack[MAXN],cnt;
    int sum[MAXN],val[MAXN],S;
    //sum表示子树内所有的点的异或和,val表示x加上虚儿子子树的异或和
    struct node{ int x,y,z; }e[MAXN];
    inline bool isroot(int x){ return (tr[father[x]][0]!=x) && (tr[father[x]][1]!=x); }
    inline void update(int x){ sum[x]=sum[tr[x][0]]^sum[tr[x][1]]^val[x]; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void pushdown(int x){
    	if(tag[x]==0) return ;
    	int l=tr[x][0],r=tr[x][1];
    	tag[l]^=1; tag[r]^=1; swap(tr[x][0],tr[x][1]);
    	tag[x]=0;
    }
    
    inline void rotate(int x){
    	int y=father[x],z=father[y];
    	int l,r; l=(tr[y][1]==x); r=l^1;
    	if(!isroot(y)) tr[z][(tr[z][1]==y)]=x;
    	father[x]=z; father[y]=x;
    	tr[y][l]=tr[x][r]; father[tr[x][r]]=y; tr[x][r]=y;
    	update(y); update(x);
    }
    
    inline void splay(int x){
    	top=0; stack[++top]=x;
    	for(int i=x;!isroot(i);i=father[i]) stack[++top]=father[i];
    	for(int i=top;i>=1;i--) pushdown(stack[i])/*!!!不是top啊!!!*/;
    	int y,z;
    	while(!isroot(x)) {
    		y=father[x]; z=father[y];
    		if(!isroot(y)) {
    			if((tr[z][0]==y) ^ (tr[y][0]==x)) rotate(x);
    			else rotate(y);
    		}
    		rotate(x);
    	}
    }
    
    inline void access(int x){
    	int last=0;
    	while(x){
    		splay(x);
    		val[x]^=sum[tr[x][1]];//把原来的右儿子加入虚儿子的统计中
    		//而对于sum数组即整个子树没有变化,所以无需考虑
    
    		val[x]^=sum[last];//把原来是虚儿子的去掉
    		tr[x][1]=last;//!!!
    		update(x);//!!!顺序
    
    		last=x; x=father[x];
    	}
    }
    
    inline void move_to_root(int x){
    	access(x);
    	splay(x);
    	tag[x]^=1;
    }
    
    inline void link(int x,int y){
    	move_to_root(x);
    	move_to_root(y);//!!!必须要把y也变成根,不然修改了y之后修改不到y的祖先,把y变成根节点就可以只修改自己了
    	father[x]=y;  val[y]^=sum[x];//加入x的贡献
    	update(y);
    }
    
    inline void cut(int x,int y){
    	move_to_root(x);
    	access(y);
    	splay(y);
    	tr[y][0]=father[x]=0;
    	update(y);//此处update的时候也可以消除x的贡献
    }
    
    inline void modify(int x,int vaL){
    	access(x); splay(x);
    	val[x]^=vaL; sum[x]^=vaL;//只要修改自己就好了
    }
    
    inline bool query(int x,int y){
    	move_to_root(x); access(y);
    	return val[y]==S?1:0;
    }
    
    inline void work(){
    	srand(time(NULL));
    	ID=getint(); n=getint(); m=getint(); int type,x,y;
    	for(int i=1;i<n;i++) x=getint(),y=getint(),link(x,y);
    	while(m--) {
    		type=getint();
    		if(type==1) {
    			x=getint(); y=getint(); cut(x,y);
    			x=getint(); y=getint(); link(x,y);
    		}
    		else if(type==2) {
    			x=getint(); y=getint();
    			e[++cnt].x=x;/*!!!*/ e[cnt].y=y; e[cnt].z=(rand()%10000+1)*(rand()%100000+1);
    			modify(x,e[cnt].z); modify(y,e[cnt].z); S^=e[cnt].z;
    		}
    		else if(type==3) {
    			x=getint();	S^=e[x].z;
    			modify(e[x].x,e[x].z); modify(e[x].y,e[x].z); 
    		}
    		else {
    			x=getint(); y=getint();
    			if(query(x,y)) puts("YES");
    			else puts("NO");
    		}
    	}
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("207.in","r",stdin);
    	freopen("207.out","w",stdout);
    #endif
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    格式化HDFS出现java.io.IOException: Cannot create directory /opt/hdfs/name/current错误
    Hadoop集群安装教程(完全分布模式)——更新中
    将sql语句嵌入到c语言中——codeblocks
    centos虚拟机网络连接问题
    Linux虚拟机如何调整登录界面的分辨率——解决登录界面图标过大的问题
    Spring Cloud常用组件超时总结
    程序集加载与反射笔记
    ASP.NET分页
    显式向标识列插入数据
    JIT和程序的首次执行
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6685006.html
Copyright © 2011-2022 走看看