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;
    }
    
  • 相关阅读:
    第三方驱动备份与还原
    Greenplum 解决 gpstop -u 指令报错
    yum安装(卸载)本地rpm包的方法(卸载本地安装的greenplum 5.19.rpm)
    Java JUC(java.util.concurrent工具包)
    netty 详解(八)基于 Netty 模拟实现 RPC
    netty 详解(七)netty 自定义协议解决 TCP 粘包和拆包
    netty 详解(六)netty 自定义编码解码器
    netty 详解(五)netty 使用 protobuf 序列化
    netty 详解(四)netty 开发 WebSocket 长连接程序
    netty 详解(三)netty 心跳检测机制案例
  • 原文地址:https://www.cnblogs.com/creating-2007/p/13196541.html
Copyright © 2011-2022 走看看