牛客多校第四场E.Xor Tree
题意,给一颗树。
- 条件一:树上的每个节点都有一个取值范围,记为(l,r)。
- 条件二:树上的每条边都有一个异或值,表示当边两端的节点在取值范围内取值的时候两个值的异或结果为多少。
求:总共有多少种取值满足条件
题解:
- 结论一:只要确定了整个树上任意的一个节点,整棵树就确定了。
- 结论二:假设存在一颗树满足条件二,则当所有的节点异或上某一个值以后仍然满足,理由是x^x=0。
- 由上述两个结论我们可以将条件二转化为每个点有一个初始值,满足当根节点(可以自定义)为0的时候,整棵树的取值(未必满足条件一),此时需要去寻找公共的x,使x异或整棵树之后的值满足条件一,求满足这个条件的x的取值范围。
对于这个条件转化,我们只需要通过树上dfs一遍就可以解决了,代码如下:
ll p[maxn];
pair<int, int> v[maxn];
vector<pair<int, int>> g[maxn];
bool used[maxn];
void init(int i, int Xor)
{
if (used[i])
return;
used[i] = 1;
p[i] = Xor;
for (auto it : g[i])
{
init(it.first, it.second ^ Xor);
}
}
之后,就是求解范围内的取值
首先我们将所有的范围和数字转化为二进制,方便我们对异或进行计算。
其次开始枚举情况:
先从左区间开始:
假设前面的01值全部都是相同的
left | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|---|---|---|---|
num | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
x | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
状态 | 搜索 | 搜索 | 不可 | 不可 | 都可 | 都可 | 搜索 | 搜索 |
什么意思呢,就是当左区间搜索到当前位之前我们假设都是完全相同的,然后如果此时我们的取值x与num异或之后仍然与left相同,那么久继续往下搜索,如果比left小那就不可能了,直接剪掉,如果比left大那后面不论怎么取值都是可以的,因此可以直接放置一个标签表示都可以。
同样的,我们对右区间进行这样的枚举,最后分别操作一遍就可以了。
具体实现可以在一个01trie树上面实现(你要说是个二叉树也行,因为确实是)
我们首先定义一个根节点,因为所有的数字小于2^30,因此根节点设置高度为30,在没有减之前我们设置所有的节点带一个满标签表示都可以,然后一步一步剪下去,最后再跑一遍整棵树计算一下权值就可以了,权值定义为2的高度次方,高度就是位的位置。
AC代码如下:
#include <bits/stdc++.h>
#define ll long long
#define cout std::cout
using namespace std;
struct node
{
ll deep;
int ok;
bool ext;
bool full;
node *l, *r;
node()
{
ext = 0;
deep = 0;
full = 1;
ok = 3;
l = nullptr;
r = nullptr;
}
node(int x) : deep(x)
{
ok = 3;
full = 1;
ext = 0;
l = nullptr;
r = nullptr;
}
} * root;
const int maxn = 1e5 + 50;
ll p[maxn];
pair<int, int> v[maxn];
vector<pair<int, int>> g[maxn];
bool used[maxn];
void init(int i, int Xor)
{
if (used[i])
return;
used[i] = 1;
p[i] = Xor;
for (auto it : g[i])
init(it.first, it.second ^ Xor);
}
void build(int P, int type, int num, int k, node *root)
{
if (!root->ext)
root->l = new node(k), root->r = new node(k), root->ext = 1;
if (k < 0 || !root->ok)
return;
root->full = 0;
bool n = num & (1 << k), p = P & (1 << k);
if (type ^ p)
if (!(type ^ n))
root->l->ok = 0, root->ok &= 1;
else
root->r->ok = 0, root->ok &= 2;
if (n ^ p)
if (root->r->ok)
build(P, type, num, k - 1, root->r);
else if (root->l->ok)
build(P, type, num, k - 1, root->l);
}
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1)
res *= a;
a *= a;
b >>= 1;
}
return res;
}
int solve(node *root)
{
int res = 0;
if (root->deep == 0)
return 1;
if (root->full && root->ok == 3)
return qpow(2, root->deep);
if (root->ok & 1)
if (root->r != nullptr)
res += solve(root->r);
if (root->ok & 2)
if (root->l != nullptr)
res += solve(root->l);
return res;
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> v[i].first >> v[i].second;
for (int i = 1; i < n; i++)
{
int x, y, c;
cin >> x >> y >> c;
g[x].push_back({y, c});
g[y].push_back({x, c});
}
root = new node();
root->deep = 30;
init(1, 0);
for (int i = 1; i <= n; i++)
{
build(v[i].first, 0, p[i], 29, root);
build(v[i].second, 1, p[i], 29, root);
}
cout << solve(root) << '\n';
}