传送门
题面信息
有一棵n个节点的异或树,
1
1
1号点为根,每个节点有一个权值
w
i
wi
wi。每次询问给出
u
u
u,
x
x
x,询问子树
u
u
u内,点的权值大于x的所有权值异或
x
x
x的和。
由于这是一棵异或树,所以,如果一个数出现了两次,那么这两个点的权值就消失了(点并没有消失,即树的形态没有发生变化,只是在计算时忽略这两个点的权值。)消失过程发生在一次询问时,如果子树内两个点的权值一样,那么这两个点的权值同时消失,直到无法再有点对消失后查询。
分析
在子树内,一个值出现了偶数次就不被计算,视为不可见,最后要求大于
x
x
x的异或的和(不是异或和)
目前有两个问题
1.如何维护子树内的异或信息?
2.如何快速求出大于
x
x
x的异或和
对于第一个问题,从思考的角度,我们确实能够
O
(
1
)
O(1)
O(1)将子树的异或统计到当前节点,直接将子节点维护的异或值异或到当前节点即可。但是我门求的是异或的和,这里就无法求到和了。为什么呢?
我们要求和
假设
v
i
vi
vi 为
u
u
u的每个可见的儿子节点的值,有
p
p
p个,
x
x
x 为本节点的值
我们要求
∑
i
=
1
p
x
⊕
v
i
\sum_{i=1}^{p}{x \oplus v_i}
∑i=1px⊕vi
单独考虑一个异或的情况,对位考虑,假设为
i
n
t
int
int类型,32位
x
⊕
v
x \oplus v
x⊕v,等价于
∑
j
=
0
31
(
x
[
j
]
⊕
v
[
j
]
)
∗
(
1
<
<
j
)
\sum_{j=0}^{31}{(x[j] \oplus v[j])*(1<<j)}
∑j=031(x[j]⊕v[j])∗(1<<j)
仔细想想是否这样
所以带到上一个式子中,求异或的和,等价于
∑
i
=
1
p
∑
j
=
0
31
(
x
[
j
]
⊕
v
i
[
j
]
)
∗
(
1
<
<
j
)
\sum_{i=1}^{p}{\sum_{j=0}^{31}{(x[j] \oplus v_i[j])*(1<<j)}}
∑i=1p∑j=031(x[j]⊕vi[j])∗(1<<j)
前面的求和和后面的求和可以交换
也就等价于
∑
j
=
0
31
∑
i
=
1
p
(
x
[
j
]
⊕
v
i
[
j
]
)
∗
(
1
<
<
j
)
\sum_{j=0}^{31}{\sum_{i=1}^{p}{(x[j] \oplus v_i[j])*(1<<j)}}
∑j=031∑i=1p(x[j]⊕vi[j])∗(1<<j)
对于
x
x
x相关项是我们能够已知的
所以我们只要维护
u
u
u可见儿子节点的每一位的贡献即可
最后求异或和的时候直接计算即可
接下来考虑第二个问题。如何快速求大于
K
K
K的异或和呢?
异或和能够解决了,转化为就是找到区间
K
K
K~
M
A
X
MAX
MAX的和
此时我们就要想到线段树了,接下来就好解释了。
对于每一个节点建立一颗动态开点权值线段树,每个线段树节点存
树中节点值对应的异或位信息,因为同一颗子树会出现两个相同的值
线段树合并的时候就能处理掉 不可见 的节点
代码实现如下
代码
//22527
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e5+5;
const ll MOD = 1e9+7;
int N, M, K;
int head[MAX_N];
int tot = 0;
struct Edge{
int to, nxt;
}edge[MAX_N<<1];
void addEdge(int u, int v) {
edge[tot].nxt = head[u];
edge[tot].to = v;
head[u] = tot++;
edge[tot].nxt = head[v];
edge[tot].to = u;
head[v] = tot++;
}
int arr[MAX_N];
struct Tr {
int k, l, r, sum[18];
}tr[MAX_N<<6];
int root[MAX_N];
int indx = 0;
int mk() {
return ++indx;
}
void push_up(int rt) {
int ls = tr[rt].l;
int rs = tr[rt].r;
tr[rt].k = tr[ls].k + tr[rs].k;
for (int i = 0; i <= 17; ++i) {
tr[rt].sum[i] = tr[ls].sum[i] + tr[rs].sum[i];
}
}
void update(int &rt, int l, int r, int x) {
if (!rt) rt = mk();
if (l == r) {
tr[rt].k ^= 1;
for (int i = 0; i <= 17; ++i) {
tr[rt].sum[i] = (tr[rt].k) * (x>>i&1);
}
return;
}
int mid = l + ((r-l)>>1);
if (x <= mid) update(tr[rt].l, l, mid, x);
if (x > mid) update(tr[rt].r, mid+1, r, x);
push_up(rt);
}
int merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
if (l == r) {
for (int i = 0; i <= 17; ++i) {
tr[x].sum[i] ^= tr[y].sum[i];
}
tr[x].k ^= tr[y].k;
return x;
}
int mid = l + ((r-l)>>1);
tr[x].l = merge(tr[x].l, tr[y].l, l, mid);
tr[x].r = merge(tr[x].r, tr[y].r, mid+1, r);
push_up(x);
return x;
}
int query(int rt, int l, int r, int x, int y, int k) {
if (!rt || x > y) return 0;
if (x <= l && r <= y) {
int res = 0;
for (int i = 0; i <= 17; ++i) {
if (k>>i&1) {
res += (1ll<<i) * (tr[rt].k - tr[rt].sum[i]);
} else {
res += (1ll<<i) * (tr[rt].sum[i]);
}
}
return res;
}
int mid = l + ((r-l)>>1);
if (y <= mid) return query(tr[rt].l, l, mid, x, y, k);
if (x > mid) return query(tr[rt].r, mid+1, r, x, y, k);
return query(tr[rt].l, l, mid, x, y, k) + query(tr[rt].r, mid+1, r, x, y, k);
}
struct Qr{
int k, id;
};
vector<Qr>qr[MAX_N];
int ans[MAX_N];
void dfs(int u, int from) {
int v;
for (int i = head[u];~i;i=edge[i].nxt) {
if ((v=edge[i].to) == from) continue;
dfs(v, u);
root[u] = merge(root[u], root[v], 1, N);
}
for (auto q : qr[u]) {
ans[q.id] = query(root[u], 1, N, q.k+1, N, q.k);
}
}
void init() {
memset(head, -1, sizeof head);
tot = 0;
}
void solve(){
init();
sc("%lld%lld", &N, &M);
int u, v;
for (int i = 1; i <= N; ++i) {
sc("%lld", &arr[i]);
update(root[i], 1, N, arr[i]);
}
for (int i = 2; i <= N; ++i) {
sc("%lld%lld", &u, &v);
addEdge(u, v);
}
for (int i = 1; i <= M; ++i) {
sc("%lld%lld", &u, &v);
qr[u].push_back({v, i});
}
dfs(1, 0);
for (int i = 1; i <= M; ++i) {
pr("%lld\n", ans[i]);
}
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}