zoukankan      html  css  js  c++  java
  • [UOJ#207]共价大爷游长沙

    [UOJ#207]共价大爷游长沙

    试题描述

    火车司机出秦川,跳蚤国王下江南,共价大爷游长沙。每个周末,勤劳的共价大爷都会开车游历长沙市。

    长沙市的交通线路可以抽象成为一个 (n) 个点 (n-1) 条边的无向图,点编号为 (1)(n),任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连。有一个包含一些点对 ((x,y))可重集合 (S),共价大爷的旅行路线是这样确定的:每次他会选择 (S) 中的某一对点 ((x,y)),并从 (x) 出发沿着唯一路径到达 (y)

    小L是共价大爷的脑残粉,为了见到共价大爷的尊容,小L决定守在这张图的某条边上等待共价大爷的到来。为了保证一定能见到他,显然小L必须选择共价大爷一定会经过的边——也就是所有共价大爷可能选择的路径都经过的边。

    现在小L想知道,如果他守在某一条边,是否一定能见到共价大爷。

    然而长沙市总是不断的施工,也就是说,可能某个时刻某条边会断开,同时这个时刻一定也有某条新边会出现,且任意时刻图都满足任意两点间均存在恰好一条路径的条件。注意断开的边有可能和加入的新边连接着相同的两个端点。共价大爷的兴趣也会不断变化,所以 (S) 也会不断加入新点对或者删除原有的点对。当然,小L也有可能在任何时候向你提出守在某一条边是否一定能见到共价大爷的问题。你能回答小L的所有问题吗?

    输入

    输入的第一行包含一个整数 ( exttt{id}),表示测试数据编号,如第一组数据的 ( exttt{id}=1),样例数据的 ( exttt{id}) 可以忽略。hack数据中的 ( exttt{id}) 必须为 (0)(10) 之间的整数。hack数据中 ( exttt{id}) 的值和数据类型没有任何关系。

    输入的第二行包含两个整数 (n,m),分别表示图中的点数,以及接下来会发生的事件数,事件的定义下文中会有描述。初始时 (S) 为空。

    接下来 (n−1) 行,每行两个正整数 (x,y),表示点 (x) 和点 (y) 之间有一条无向边。

    接下来 (m) 行,每行描述一个事件,每行的第一个数 ( exttt{type}) 表示事件的类型。

    ( exttt{type}=1),那么接下来有四个正整数 (x,y,u,v),表示先删除连接点 (x) 和点 (y) 的无向边,保证存在这样的无向边,然后加入一条连接点 (u) 和点 (v) 的无向边,保证操作后的图仍然满足题中所述条件。

    ( exttt{type}=2),那么接下来有两个正整数 (x,y),表示在 (S) 中加入点对 ((x,y))

    ( exttt{type}=3),那么接下来有一个正整数 (x),表示删除第 (x) 个加入 (S) 中的点对,即在第 (x)( exttt{type}=2) 的事件中加入 (S) 中的点对,保证这个点对存在且仍然在 (S) 中。

    ( exttt{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
    

    输出示例

    YES
    NO
    YES
    

    数据规模及约定

    对于 (100\%) 的数据,(n le 10^5)(m le 3 imes 10^5)

    题解

    原来随机权值再异或是靠谱的。。。

    做法是这样的,我们需要一个方法能够快速实现查询某个集合是否为全集,我们不妨先想想 (|S| le 10) 的情况。这样一个集合就可以用二进制表示成一个 ([0, 1023]) 的整数,然后增一个元素或者去掉一个已有元素时就直接异或。那么现在我们可以沿用这个异或的办法,随机给每个新加的点对安排一个权值,出错率很低(具体多低不知道,实验证明可行。。。)

    然后剩下的部分就是 LCT 咯。加入一个点对 ((u, v)) 的时候,把 (u)(v) 路径上的全都异或上一个值,换边操作就是将被删边的权值异或到被删边两端点在新树上路径就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <unordered_map>
    #include <map>
    #include <ctime>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 500010
    #define oo 2147483647
    #define RND rand()
    
    int n, all;
    
    struct LCT {
    	int fa[maxn], ch[maxn][2], val[maxn], Xor[maxn], tag[maxn], S[maxn], top;
    	bool rev[maxn], siz[maxn];
    	
    	bool isrt(int u) { return ch[fa[u]][0] != u && ch[fa[u]][1] != u; }
    	
    	void maintain(int u) {
    		Xor[u] = val[u]; siz[u] = u > n;
    		if(ch[u][0]) Xor[u] ^= Xor[ch[u][0]], siz[u] ^= siz[ch[u][0]];
    		if(ch[u][1]) Xor[u] ^= Xor[ch[u][1]], siz[u] ^= siz[ch[u][1]];
    		Xor[u] ^= tag[u] * (siz[u] ^ (u > n));
    		return ;
    	}
    	void pushdown(int u) {
    		if(rev[u]) {
    			if(ch[u][0]) rev[ch[u][0]] ^= 1;
    			if(ch[u][1]) rev[ch[u][1]] ^= 1;
    			swap(ch[u][0], ch[u][1]);
    			rev[u] = 0;
    		}
    		if(tag[u]) {
    			if(ch[u][0]) {
    				tag[ch[u][0]] ^= tag[u];
    				if(ch[u][0] > n) val[ch[u][0]] ^= tag[u];
    				Xor[ch[u][0]] ^= tag[u] * siz[ch[u][0]];
    			}
    			if(ch[u][1]) {
    				tag[ch[u][1]] ^= tag[u];
    				if(ch[u][1] > n) val[ch[u][1]] ^= tag[u];
    				Xor[ch[u][1]] ^= tag[u] * siz[ch[u][1]];
    			}
    			tag[u] = 0;
    			maintain(u);
    		}
    		return ;
    	}
    	void rotate(int u) {
    		int y = fa[u], z = fa[y], l = 0, r = 1;
    		if(!isrt(y)) ch[z][ch[z][1]==y] = u;
    		if(ch[y][1] == u) swap(l, r);
    		fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
    		ch[y][l] = ch[u][r]; ch[u][r] = y;
    		return maintain(y);
    	}
    	void splay(int u) {
    		int t = u;
    		while(!isrt(t)) S[++top] = t, t = fa[t]; S[++top] = t;
    		while(top) pushdown(S[top--]);
    		while(!isrt(u)) {
    			int y = fa[u], z = fa[y];
    			if(!isrt(y)) {
    				if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
    				else rotate(y);
    			}
    			rotate(u);
    		}
    		return maintain(u);
    	}
    	
    	void access(int u) {
    		splay(u); ch[u][1] = 0; maintain(u);
    		while(fa[u]) splay(fa[u]), ch[fa[u]][1] = u, maintain(fa[u]), splay(u);
    		return ;
    	}
    	void makert(int u) {
    		access(u); rev[u] ^= 1;
    		return ;
    	}
    	void link(int a, int b) {
    		makert(b); fa[b] = a;
    		return ;
    	}
    	void cut(int a, int b) {
    		makert(a); access(b); fa[a] = ch[b][0] = 0;
    		return maintain(b);
    	}
    	void cover(int a, int b, int v) {
    		makert(a); access(b); tag[b] ^= v; if(b > n) val[b] ^= v; Xor[b] ^= v * siz[b];
    		return ;
    	}
    	int query(int a, int b) {
    		makert(a); access(b);
    		return Xor[b];
    	}
    } sol;
    
    #define pii pair <int, int>
    #define x first
    #define y second
    #define mp(x, y) make_pair(x, y)
    
    unordered_map <int, bool> has;
    map <pii, int> eid;
    struct Pair {
    	int u, v, rnd;
    	Pair() {}
    	Pair(int _1, int _2, int _3): u(_1), v(_2), rnd(_3) {}
    } ps[maxn];
    int cntp, cnte;
    
    int main() {
    	srand((unsigned)time(0));
    	read();
    	n = read(); int q = read();
    	rep(i, 1, n - 1) {
    		int u = read(), v = read();
    		if(u > v) swap(u, v);
    		eid[mp(u,v)] = ++cnte;
    		sol.link(u, cnte + n); sol.link(cnte + n, v);
    	}
    	rep(kase, 1, q) {
    		int tp = read(), u, v;
    		if(tp == 1) {
    			u = read(); v = read(); if(u > v) swap(u, v);
    			int id = eid[mp(u,v)], nu = read(), nv = read(), nid, Xor = sol.query(u, v);
    			if(nu > nv) swap(nu, nv); nid = eid.count(mp(nu, nv)) ? eid[mp(nu,nv)] : (eid[mp(nu,nv)] = ++cnte);
    			sol.cut(u, id + n); sol.cut(id + n, v);
    			sol.val[nid+n] = sol.tag[nid+n] = sol.Xor[nid+n] = 0;
    			sol.link(nu, nid + n); sol.link(nid + n, nv);
    			sol.cover(u, v, Xor);
    		}
    		if(tp == 2) {
    			u = read(); v = read();
    			int rnd = RND;
    			while(has.count(rnd)) rnd = RND;
    			has[rnd] = 1;
    			sol.cover(u, v, rnd);
    			ps[++cntp] = Pair(u, v, rnd); all ^= rnd;
    		}
    		if(tp == 3) {
    			u = read();
    			sol.cover(ps[u].u, ps[u].v, ps[u].rnd); all ^= ps[u].rnd;
    		}
    		if(tp == 4) {
    			u = read(); v = read();
    			puts(sol.query(u, v) == all ? "YES" : "NO");
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Javascript创建对象的几种方式【转】
    log4net轻松使用日期作为动态文件名【转】
    使用jquery的lazy loader插件实现图片的延迟加载
    Oracle通用分页存储过程的创建与使用
    Oracle 11g R2的卸载与重装
    Oracle的rownum的原理和使用【转】
    Remoting客户端和服务端两种方式的调用总结
    动态执行SQL语句
    Trie模板
    Dijkstrapriority_queue
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8027824.html
Copyright © 2011-2022 走看看