zoukankan      html  css  js  c++  java
  • 【题解】P6098 [USACO19FEB] Cow Land G

    题目传送门

    题意

    给定一颗树,支持单点修改、树上路径异或和查询。

    题解

    这是一道重链剖分的裸题。

    重链剖分是将每个节点的子树大小最大的儿子节点作为重儿子,其余作为轻儿子。

    父节点与其重儿子的连边称为重边

    然后将整棵树重新编号,把整棵树拆成许多重链(由重边相连得到的链)和 轻链(由非重边相连得到的链)。

    重新编号之后,使用树状数组线段树维护即可。如果不了解重链剖分,不妨看看这篇博客

    本题需要支持单点修改、树上路径异或和查询,可以使用线段树维护。

    复杂度(mathcal{O}(qlog ^2n))

    注意异或操作在C++中表示为^

    代码

    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    #define in __inline__
    typedef long long ll;
    in ll max(ll x, ll y){return x > y ? x : y;}
    in ll min(ll x, ll y){return x < y ? x : y;}
    in void swap(ll &x, ll &y){x ^= y ^= x ^= y;}
    #define rei register int
    #define FOR(i, l, r) for(rei i = l; i <= r; ++i)
    #define FOL(i, r, l) for(rei i = r; i >= l; --i)
    char inputbuf[1 << 23], *p1 = inputbuf, *p2 = inputbuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = inputbuf) + fread(inputbuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    in ll read() {
    	ll res = 0; char ch = getchar(); bool f = true;
    	for(; ch < '0' || ch > '9'; ch = getchar())
    		if(ch == '-') f = false;
    	for(; ch >= '0' && ch <= '9'; ch = getchar())
    		res = res * 10 + (ch ^ 48);
    	return f ? res : -res;
    }
    const int N = 1e5 + 15;
    
    int a[N], v[N], sum[N << 2], fr[N << 1], to[N << 1], nxt[N << 1], head[N], cnt;
    int dep[N], id[N], top[N], fa[N], siz[N], son[N], tot, n, x, y;
    /*
    dep[i]:节点i的深度
    id[i]:重新编号后节点i的编号
    top[i]:包括节点i的重链顶端
    siz[i]:以i为根的子树大小
    son[i]:节点i的重儿子
    fa[i]:节点i的父亲
    v[i]:重新编号后编号为i的节点的值
    */
    
    in void add(int x, int y) {to[++cnt] = y, nxt[cnt] = head[x], head[x] = cnt;}
    in void upd(int k) {sum[k] = sum[k << 1] ^ sum[k << 1 | 1];}
    
    void dfs1(int u, int f) {//处理各种信息,包括深度、父节点、子树大小、重儿子
    	int v; fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
    	for(rei i = head[u]; i; i = nxt[i]) if((v = to[i]) ^ f) {
    		dfs1(v, u); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v;
    	}
    }
    void dfs2(int u, int tf) {//重新编号
    	id[u] = ++tot, v[tot] = a[u]; top[u] = tf;
    	if(!son[u]) return; dfs2(son[u], tf);
    	for(rei i = head[u]; i; i = nxt[i]) if(!id[to[i]]) dfs2(to[i], to[i]);
    }
    
    void bd(int k, int l, int r) {
    	if(l == r) {sum[k] = v[l]; return;}
    	int m = l + r >> 1;
    	bd(k << 1, l, m); bd(k << 1 | 1, m + 1, r); upd(k);
    }
    void cg(int k, int l, int r) {
    	if(l == r) {sum[k] = y; return;}
    	int m = l + r >> 1;
    	id[x] <= m ? cg(k << 1, l, m) : cg(k << 1 | 1, m + 1, r); upd(k);
    }
    int qr(int k, int l, int r, int x, int y) {
    	if(x <= l && r <= y) return sum[k];
    	int m = l + r >> 1, res = 0;
    	if(x <= m) res ^= qr(k << 1, l, m, x, y);
    	if(m < y) res ^= qr(k << 1 | 1, m + 1, r, x, y);
    	return res;
    }
    int query(int x, int y, int res = 0) {//树上路径查询
    	for(; top[x] ^ top[y]; y = fa[top[y]]) {
    		if(dep[top[x]] > dep[top[y]]) x ^= y ^= x ^= y;
    		res ^= qr(1, 1, n, id[top[y]], id[y]);
    	}
    	if(id[x] > id[y]) x ^= y ^= x ^= y;
    	return (res ^= qr(1, 1, n, id[x], id[y]));
    }
    
    signed main() {
    	int q, opt;
    	n = read(); q = read();
    	FOR(i, 1, n) a[i] = read();
    	FOR(i, 1, n - 1) x = read(), y = read(), add(x, y), add(y, x);
    	dfs1(1, 0);
    	dfs2(1, 1);
    	bd(1, 1, n);
    	for(; q; --q) {
    		opt = read(), x = read(), y = read();
    		if(opt == 1) cg(1, 1, n);
    		else printf("%d
    ", query(x, y));
    	}
    	return 0;
    }
    
  • 相关阅读:
    git 常用命令
    目录
    算法--双栈排序
    算法--栈的翻转练习题
    算法--双栈队列
    算法--可查询最值的栈练习题
    Spark算子--union、intersection、subtract
    Spark算子--take、top、takeOrdered
    Spark算子--countByKey
    Spark算子--SortBy
  • 原文地址:https://www.cnblogs.com/creating-2007/p/13196541.html
Copyright © 2011-2022 走看看