bzoj2819 DFS序 + LCA + 线段树
https://www.lydsy.com/JudgeOnline/problem.php?id=2819
题意:树上单点修改及区间异或和查询。
思维难度不高,但是题比较硬核。
整体思路是维护每一个结点到根节点的距离。查询u,v树链上的异或和就是query(v) ^ query(u) ^ a[lca(u,v)],所以就要想办法维护树上的结点到根节点的异或和。
网上的题解大多是选择直接维护答案,修改的时候修改整颗子树的答案,用线段树或者树状数组区间异或一下l到r内的答案就可以。
我一开始没有想到直接上lca直接维护根节点的距离,所以在dfs序上见了一颗线段树,查询的时候query 1到u第一次出现地方的异或和,区间的时候常规单点修改也可过。
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-'0',c=getchar());return now;} #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 5e5 + 10; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,K; LL a[maxn]; struct Edge{ int next,to; }edge[maxn * 2]; int head[maxn],tot; void init(){ for(int i = 1; i <= N ; i ++) head[i] = -1; tot = 0; } void add(int u,int v){ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } const int SP = 20; int pa[maxn][SP],dep[maxn]; //求dfs序 int num; int dfs_index[maxn * 2]; PII pos[maxn]; void dfs(int t,int la){ pa[t][0] = la; dep[t] = dep[la] + 1; for(int i = 1; i <= SP - 1; i ++) pa[t][i] = pa[pa[t][i - 1]][i - 1]; pos[t].fi = ++num; dfs_index[num] = t; for(int i = head[t]; ~i ; i = edge[i].next){ int v = edge[i].to; if(v == la) continue; dfs(v,t); } pos[t].se = ++num; dfs_index[num] = t; } int lca(int u,int v){ if(dep[u] < dep[v]) swap(u,v); int t = dep[u] - dep[v]; for(int i = 0 ; i <= SP - 1; i ++){ if(t & (1 << i)) u = pa[u][i]; } for(int i = SP - 1; i >= 0 ; i --){ int uu = pa[u][i],vv = pa[v][i]; if(uu != vv){ u = uu; v = vv; } } return u == v? u :pa[u][0]; } //线段树 struct Tree{ LL sum; }tree[maxn << 3]; void Pushup(int t){ tree[t].sum = tree[t << 1].sum ^ tree[t << 1 | 1].sum; } void Build(int t,int l,int r){ if(l == r){ tree[t].sum = a[dfs_index[l]]; return; } int m = (l + r) >> 1; Build(t << 1,l,m); Build(t << 1 | 1,m + 1,r); Pushup(t); } void update(int t,int p,int x,int L,int R){ if(L == R){ tree[t].sum = x; return; } int m = (L + R) >> 1; if(p <= m) update(t << 1,p,x,L,m); else update(t << 1 | 1,p,x,m + 1,R); Pushup(t); } LL query(int t,int l,int r,int L,int R){ if(l <= L && R <= r){ return tree[t].sum; } int m = (L + R) >> 1; if(r <= m) return query(t << 1,l,r,L,m); else if(l > m) return query(t << 1 | 1,l,r,m + 1,R); else return query(t << 1,l,m,L,m) ^ query(t << 1 | 1,m + 1,r,m + 1,R); } LL query(int x){ return query(1,1,pos[x].fi,1,2 * N); } int main(){ Sca(N); init(); For(i,1,N) Scl(a[i]); For(i,1,N - 1){ int u,v; Sca2(u,v); add(u,v); add(v,u); } dfs(1,0); Build(1,1,2 * N); Sca(K); while(K--){ char op[3]; int u,v; scanf("%s%d%d",op,&u,&v); if(op[0] == 'Q'){ if(query(u) ^ query(v) ^ a[lca(u,v)]) puts("Yes"); else puts("No"); }else{ update(1,pos[u].fi,v,1,2 * N); update(1,pos[u].se,v,1,2 * N); a[u] = v; } } return 0; }