zoukankan      html  css  js  c++  java
  • UOJ #207. 共价大爷游长沙(LCT + 异或哈希)

    题目

    维护一颗动态树,并维护一个点对集合 (S)

    动态查询一条边,是否被集合中所有点对构成的路径包含。

    (n le 100000, m le 300000)

    题解

    orz 前辈 毛爷爷。

    一个很有意思的 trick 。

    如果一条边,被一条路径包含,那么两个端点分别存在与这条边对应的两个子树内。

    我们就可以利用这个巧妙的性质来做了。

    我们每次给两个端点异或上一个随机的权值,然后就可以每次查询这条边对应的任意一颗子树内所有点异或和 (res) ,如果 (res) 不等于前面所有操作的异或和,那么就是错的,否则就是正确的。

    这利用了异或的自反性,如果两个端点都在子树中那么贡献就会抵消掉,所以就是不合法的。

    可以证明这个正确率会非常的高。

    至于维护子树信息,只需要 (lct) 多维护一个虚子树信息就行了,也就是对于 (access,link,cut,pushup) 多进行一些操作就行了。

    总结

    对于有些动态树路径的题,可以考虑只维护两个端点,不用维护整个路径上的信息。

    然后可以利用异或的自反性,来保证集合中每个数出现并且仅出现一(奇数)次。

    代码

    具体实现就在代码里面了。

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
    
    using namespace std;
    
    typedef unsigned int ui;
    
    template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
    template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
    	int x(0), sgn(1); char ch(getchar());
    	for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    	for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    	return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("207.in", "r", stdin);
    	freopen ("207.out", "w", stdout);
    #endif
    }
    
    const int N = 1e5 + 1e3;
    
    #define ls(o) ch[o][0]
    #define rs(o) ch[o][1]
    
    template<int Maxn>
    struct Link_Cut_Tree {
    
    	int ch[Maxn][2], fa[Maxn];
    
    	inline bool is_root(int o) { 
    		return ls(fa[o]) != o && rs(fa[o]) != o; 
    	}
    
    	inline bool get(int o) { 
    		return rs(fa[o]) == o; 
    	}
    
    	ui val[Maxn], sub[Maxn], isub[Maxn];
    
    	inline void push_up(int o) {
    		sub[o] = sub[ls(o)] ^ sub[rs(o)] ^ isub[o] ^ val[o];
    	}
    
        void rotate(int v) {
            int u = fa[v], t = fa[u], d = get(v);
            fa[ch[u][d] = ch[v][d ^ 1]] = u;
            fa[v] = t; if (!is_root(u)) ch[t][rs(t) == u] = v;
            fa[ch[v][d ^ 1] = u] = v;
            push_up(u); push_up(v);
        }
    
    	bool rev[Maxn]; 
    
    	inline void Get_Rev(int o) { 
    		rev[o] ^= 1; swap(ls(o), rs(o)); 
    	}
    
    	inline void push_down(int o) {
    		if (rev[o])
    			Get_Rev(ls(o)), Get_Rev(rs(o)), rev[o] = false;
    	}
    
    	void Push_All(int o) {
    		if (!is_root(o)) Push_All(fa[o]); push_down(o);
    	}
    
    	inline void Splay(int o) {
    		Push_All(o);
    		for (; !is_root(o); rotate(o))
    			if (!is_root(fa[o])) rotate(get(o) != get(fa[o]) ? o : fa[o]);
    	}
    
    	inline void Access(int o) {
    		for (int t = 0; o; o = fa[t = o])
    			Splay(o), isub[o] ^= sub[rs(o)], isub[o] ^= sub[rs(o) = t], push_up(o);
    	}
    
    	inline void Make_Root(int o) {
    		Access(o); Splay(o); Get_Rev(o);
    	}
    
    	inline void Split(int u, int v) {
    		Make_Root(u); Access(v); Splay(v);
    	}
    
    	inline void Link(int u, int v) {
    		Split(u, v); fa[u] = v; isub[v] ^= sub[u]; push_up(v);
    	}
    
    	inline void Cut(int u, int v) {
    		Split(u, v); ls(v) = fa[u] = 0; push_up(v);
    	}
    
    };
    
    Link_Cut_Tree<N> T;
    
    inline void Update(int o, ui val) {
    	T.Access(o); T.Splay(o); T.val[o] ^= val; T.push_up(o);
    }
    
    random_device Rand;
    
    pair<int, int> Up[N * 3]; ui Val[N * 3], len = 0;
    
    int main () {
    
    	File();
    
    	read();
    
    	int n = read(), m = read();
    
    	For (i, 1, n - 1) T.Link(read(), read());
    
    	ui cur = 0;
    	For (i, 1, m) {
    
    		int opt = read();
    		if (opt == 1) {
    			int x = read(), y = read(), u = read(), v = read();
    			T.Cut(x, y); T.Link(u, v);
    		}
    		if (opt == 2) {
    			int x = read(), y = read(); Val[++ len] = Rand();
    			cur ^= Val[len]; Update(x, Val[len]); Update(y, Val[len]);
    			Up[len] = make_pair(x, y);
    		}
    		if (opt == 3) {
    			int id = read(), x = Up[id].first, y = Up[id].second;
    			cur ^= Val[id]; Update(x, Val[id]); Update(y, Val[id]);
    		}
    		if (opt == 4) {
    			int x = read(), y = read(); 
    			T.Split(x, y); 
    			puts(T.sub[x] == cur ? "YES" : "NO");
    		}
    	}
    
    	return 0;
    
    }
    
  • 相关阅读:
    奇异值分解
    特征值和特征向量
    矩阵
    矢量化
    符号数组
    通用函数
    数据平滑
    多项式拟合
    协方差/相关矩阵/相关系数
    json
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9725091.html
Copyright © 2011-2022 走看看