[BZOJ2908]又是nand
[BZOJ2908]又是nand
试题描述
首先知道A nand B=not(A and B) (运算操作限制了数位位数为K)比如2 nand 3,K=3,则2 nand 3=not (2 and 3)=not 2=5。
给出一棵树,树上每个点都有点权,定义树上从a到b的费用为0与路径上的点的权值顺次nand的结果,例如:从2号点到5号点顺次经过2->3->5,权值分别为5、7、2,K=3,那么最终结果为0 nand 5 nand 7 nand 2=7 nand 7 nand 2=0 nand 2=7,现在这棵树需要支持以下操作。
① Replace a b:将点a(1≤a≤N)的权值改为b。
② Query a b:输出点a到点b的费用。
请众神给出一个程序支持这些操作。
输入
第一行N,M,K,树的节点数量、总操作个数和运算位数。
接下来一行N个数字,依次表示节点i的权值。
接下来N-1行,每行两个数字a,b(1≤a,b≤N)表示a,b间有一条树边。
接下来M行,每行一个操作,为以上2类操作之一。
输出
对于操作②每个输出一行,如题目所述。
输入示例
3 3 3 2 7 3 1 2 2 3 Query 2 3 Replace 1 3 Query 1 1
输出示例
4 7
数据规模及约定
100%的数据N、M≤100000,K≤32
题解
线段树题强行套在树上。。。
注意到 nand 运算没有结合律,所以不能直接维护。进一步分析发现 0 nand x = x nand 0 = 1 (x ∈ {0, 1}),所以我们只用关心一条路径上最后一个 0 的位置,然后数它后面 1 的个数,分奇偶性讨论一下就可以了。注意整条链权值都是 1 的情况特殊处理一下。什么你说我上面只讨论了一位二进制的情况?因为最多不超过 32 位,所以我们建 32 棵线段树就好了。
还有,因为询问有方向,所以线段树需要维护区间中最后一个和最靠前一个 0 的位置。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; #define LL long long const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } LL read() { LL 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 100005 #define maxm 200005 int n, q, k, m, head[maxn], next[maxm], to[maxm]; LL val[maxn]; void AddEdge(int a, int b) { to[++m] = b; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; next[m] = head[a]; head[a] = m; return ; } int siz[maxn], son[maxn], fa[maxn], dep[maxn], top[maxn], pos[maxn], w, qos[maxn]; void build(int u) { siz[u] = 1; for(int e = head[u]; e; e = next[e]) if(fa[u] != to[e]) { fa[to[e]] = u; dep[to[e]] = dep[u] + 1; build(to[e]); siz[u] += siz[to[e]]; if(!son[u] || siz[son[u]] < siz[to[e]]) son[u] = to[e]; } return ; } void build(int u, int tp) { pos[u] = ++w; top[u] = tp; qos[w] = u; if(son[u]) build(son[u], tp); for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u] && to[e] != son[u]) build(to[e], to[e]); return ; } int mx[33][maxn<<2], mn[33][maxn<<2]; LL V[maxn]; void build(int t, int L, int R, int o) { if(L == R) { int x = V[L] - (V[L] >> (LL)t + 1ll << (LL)t + 1ll) >> (LL)t; if(x) mx[t][o] = -1, mn[t][o] = n + 1; else mx[t][o] = mn[t][o] = L; return ; } int M = L + R >> 1, lc = o << 1, rc = lc | 1; build(t, L, M, lc); build(t, M+1, R, rc); mx[t][o] = max(mx[t][lc], mx[t][rc]); mn[t][o] = min(mn[t][lc], mn[t][rc]); return ; } void update(int t, int L, int R, int o, int p) { if(L == R) { int x = V[L] - (V[L] >> (LL)t + 1ll << (LL)t + 1ll) >> (LL)t; if(x) mx[t][o] = -1, mn[t][o] = n + 1; else mx[t][o] = mn[t][o] = L; return ; } int M = L + R >> 1, lc = o << 1, rc = lc | 1; if(p <= M) update(t, L, M, lc, p); else update(t, M+1, R, rc, p); mx[t][o] = max(mx[t][lc], mx[t][rc]); mn[t][o] = min(mn[t][lc], mn[t][rc]); return ; } int ql, qr; int querymx(int t, int L, int R, int o) { if(ql <= L && R <= qr) return mx[t][o]; int M = L + R >> 1, lc = o << 1, rc = lc | 1, ans = -1; if(ql <= M) ans = max(ans, querymx(t, L, M, lc)); if(qr > M) ans = max(ans, querymx(t, M+1, R, rc)); return ans; } int querymn(int t, int L, int R, int o) { if(ql <= L && R <= qr) return mn[t][o]; int M = L + R >> 1, lc = o << 1, rc = lc | 1, ans = n + 1; if(ql <= M) ans = min(ans, querymn(t, L, M, lc)); if(qr > M) ans = min(ans, querymn(t, M+1, R, rc)); return ans; } int lca(int a, int b) { int f1 = top[a], f2 = top[b]; // puts("lcahere"); while(f1 != f2) { // printf("%d %d %d %d\n", a, f1, b, f2); if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b); a = fa[f1]; f1 = top[a]; } if(dep[a] > dep[b]) swap(a, b); return a; } int last[40], last2[40]; LL query(int a, int b) { memset(last, -1, sizeof(last)); memset(last2, -1, sizeof(last2)); int c = lca(a, b), f1 = top[a], f2 = top[b], A = a, B = b; // puts("here"); while(f1 != top[c]) { ql = pos[f1]; qr = pos[a]; for(int i = 0; i < k; i++) { int tmp = querymn(i, 1, n, 1); if(tmp <= n) last[i] = qos[tmp]; } a = fa[f1]; f1 = top[a]; } ql = pos[c]; qr = pos[a]; for(int i = 0; i < k; i++) { int tmp = querymn(i, 1, n, 1); if(tmp <= n) last[i] = qos[tmp]; } while(f2 != top[c]) { ql = pos[f2]; qr = pos[b]; for(int i = 0; i < k; i++) { int tmp = querymx(i, 1, n, 1); if(last2[i] < 0 && tmp >= 0) last2[i] = qos[tmp]; } b = fa[f2]; f2 = top[b]; } ql = pos[c]; qr = pos[b]; for(int i = 0; i < k; i++) { int tmp = querymx(i, 1, n, 1); if(last2[i] < 0 && tmp >= 0) last2[i] = qos[tmp]; } // for(int i = 0; i < k; i++) printf("%d ", last[i]); putchar('\n'); // for(int i = 0; i < k; i++) printf("%d ", last2[i]); putchar('\n'); LL ret = 0; // printf("lca: %d\n", c); for(int i = 0; i < k; i++) { int dis; if(last[i] < 0 && last2[i] < 0) dis = dep[A] + dep[B] - dep[c] - dep[c]; else { if(last2[i] >= 0) dis = dep[B] - dep[last2[i]]; else dis = dep[last[i]] + dep[B] - dep[c] - dep[c]; } // printf("%d ", dis); ret |= ((((LL)dis & 1ll) ^ 1ll) << (LL)i); } // putchar('\n'); return ret; } int main() { n = read(); q = read(); k = read(); for(int i = 1; i <= n; i++) val[i] = read(); for(int i = 1; i < n; i++) { int a = read(), b = read(); AddEdge(a, b); } build(1); build(1, 1); for(int i = 1; i <= n; i++) V[pos[i]] = val[i]; for(int i = 0; i < k; i++) build(i, 1, n, 1); while(q--) { char tp = Getchar(); while(!isalpha(tp)) tp = Getchar(); LL a = read(), b = read(); if(tp == 'Q') printf("%lld\n", query(a, b)); if(tp == 'R') { V[pos[a]] = b; for(int i = 0; i < k; i++) update(i, 1, n, 1, pos[a]); } } return 0; }