P6018 [Ynoi2010] Fusion tree
操作与一个点相邻的所有点,考虑以 为根 DFS,然后将每个这样的点分为其父亲和儿子考虑。注意到父亲只有一个,考虑只维护儿子的答案,父亲特判处理一下即可。
考虑我们要做什么操作?单点修改,全局 ,求全局异或值。考虑 01-Trie 维护。单点修改就是删一个数加一个数,容易维护,全局异或值可以考虑算每一位的贡献。具体地,插入时从低位到高位插入,考虑每个点维护以这个点为根的插入时结尾点数量,pushup 时直接合并左右两子树的点数量。同时维护 表示以这个点为根的子树全部数异或,上传时考虑往 这条边走时,如果点数是奇数就加上贡献。具体可以看代码:
void pushup(int u)
{
tr[u].sum = tr[u].cnt = 0;
if (tr[u].son[0])
{
tr[u].cnt += tr[tr[u].son[0]].cnt;
tr[u].sum = tr[tr[u].son[0]].sum << 1;
}
if (tr[u].son[1])
{
tr[u].cnt += tr[tr[u].son[1]].cnt;
tr[u].sum ^= (tr[tr[u].son[1]].sum << 1) | (tr[tr[u].son[1]].cnt & 1);
}
}
其中左移 的原因是我们从低往高位插,考虑这个点事实上是末尾加入了一个 或 。
全局异或即根节点的 值。
现在考虑全局 怎么维护。似乎有些困难,因为 01-Trie 基于二进制,于是我们考虑 在二进制下的意义。其实是将最低的 位变成 ,然后让这个位右边全都变成 。也就是将最低的 位右边全部取反。
其实就是从根节点开始,每次尝试往 的边走,如果能走就走,并且交换 和 的边。
于是整个题就做完了,特别注意处理父亲节点的贡献。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
using namespace std;
const int N = 5e5 + 5, M = 21;
int rt[N];
int fa[N];
vector<int> son[N];
vector<int> G[N];
int n, m;
int a[N];
int cnt[N], c2[N];
void dfs(int u, int f)
{
fa[u] = f;
for (auto& j : G[u])
{
if (j != f)
{
dfs(j, u);
son[u].emplace_back(j);
}
}
}
inline int qry(int u)
{
int res = a[u] + c2[u] + (fa[u] ? cnt[fa[u]] : 0);
return res;
}
class Trie
{
public:
int idx;
struct Node
{
int cnt, sum;
int son[2];
}tr[2 * N * M];
void pushup(int u)
{
tr[u].sum = tr[u].cnt = 0;
if (tr[u].son[0])
{
tr[u].cnt += tr[tr[u].son[0]].cnt;
tr[u].sum = tr[tr[u].son[0]].sum << 1;
}
if (tr[u].son[1])
{
tr[u].cnt += tr[tr[u].son[1]].cnt;
tr[u].sum ^= (tr[tr[u].son[1]].sum << 1) | (tr[tr[u].son[1]].cnt & 1);
}
}
void ins(int& rt, int x)
{
if (!rt) rt = ++idx;
int u = rt;
vector<int> v;
for (int i = 0; i <= 20; i++)
{
v.emplace_back(u);
int p = ((x >> i) & 1);
if (!tr[u].son[p]) tr[u].son[p] = ++idx;
u = tr[u].son[p];
}
tr[u].cnt++;
for (int i = v.size() - 1; i >= 0; i--) pushup(v[i]);
}
void del(int& rt, int x)
{
int u = rt;
vector<int> v;
for (int i = 0; i <= 20; i++)
{
v.emplace_back(u);
int p = ((x >> i) & 1);
u = tr[u].son[p];
}
tr[u].cnt--;
for (int i = v.size() - 1; i >= 0; i--) pushup(v[i]);
}
void add(int u)
{
swap(tr[u].son[0], tr[u].son[1]);
if (tr[u].son[0]) add(tr[u].son[0]);
pushup(u);
}
}tr;
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
dfs(1, 0);
for (int i = 1; i <= n; i++)
{
for (auto& j : son[i]) tr.ins(rt[i], a[j]);
}
while (m--)
{
int op;
cin >> op;
if (op == 1)
{
int u;
cin >> u;
tr.add(rt[u]);
int rm = qry(fa[u]);
cnt[u]++;
if (fa[u]) c2[fa[u]]++;
if (fa[fa[u]])
{
tr.del(rt[fa[fa[u]]], rm);
tr.ins(rt[fa[fa[u]]], rm + 1);
}
}
else if (op == 2)
{
int u, x;
cin >> u >> x;
int rm = qry(u);
if (fa[u])
{
tr.del(rt[fa[u]], rm);
tr.ins(rt[fa[u]], rm - x);
}
a[u] -= x;
}
else if (op == 3)
{
int u;
cin >> u;
int res = (fa[u] ? qry(fa[u]) : 0);
res ^= tr.tr[rt[u]].sum;
cout << res << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号