P6018 [Ynoi2010] Fusion tree

操作与一个点相邻的所有点,考虑以 11 为根 DFS,然后将每个这样的点分为其父亲和儿子考虑。注意到父亲只有一个,考虑只维护儿子的答案,父亲特判处理一下即可。

考虑我们要做什么操作?单点修改,全局 +1+1,求全局异或值。考虑 01-Trie 维护。单点修改就是删一个数加一个数,容易维护,全局异或值可以考虑算每一位的贡献。具体地,插入时从低位到高位插入,考虑每个点维护以这个点为根的插入时结尾点数量,pushup 时直接合并左右两子树的点数量。同时维护 ss 表示以这个点为根的子树全部数异或,上传时考虑往 11 这条边走时,如果点数是奇数就加上贡献。具体可以看代码:

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);
	}
}

其中左移 11 的原因是我们从低往高位插,考虑这个点事实上是末尾加入了一个 0011

全局异或即根节点的 sumsum 值。

现在考虑全局 +1+1 怎么维护。似乎有些困难,因为 01-Trie 基于二进制,于是我们考虑 +1+1 在二进制下的意义。其实是将最低的 00 位变成 11,然后让这个位右边全都变成 00。也就是将最低的 00 位右边全部取反。

其实就是从根节点开始,每次尝试往 11 的边走,如果能走就走,并且交换 0011 的边。

于是整个题就做完了,特别注意处理父亲节点的贡献。

#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;
}
posted @   HappyBobb  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示