题目大意
你有一个一颗树节点数,树上的边有一个边权
,点有一个点权
,你要保证有直接父子关系的点之间
,并且树上每个点他的点权选取范围为
,现在请问你能构造出多少颗不同的树,有任何一个
不同就认为是一颗不同的树。
Solution
考点:线段树区间性质
首先树上边权关系给你了,所以当我们顶下后整棵树后续的
都会被确定下来,问题就转化成
有多少种取值可能。
好,下面我们假设求一遍
,这样就可以预处理出每个点的
,我们就可以把题目的区间变成下面的形式。
那么全部已知,问题就变成了
个区间求并集的问题,并集的大小就是答案了。
但是由于是异或操作,所以本身连续的在异或
之后整个区间并不会保持连续,下面我们就要想办法处理出这些分散的区间来。
然后就要想到这个题目的难点了,用线段树的区间性质考虑新建区间,因为线段树最多会分成段区间,并且每段区间都有很明显的前后一致的相关性,那么我们就在线段树上找到这些被
包含的区间,然后把他们处理好之后塞进容器里面,最终求解有没有某些部分是
个区间相同的并集,这个用一次扫描线即可。
const int N = 1e5 + 7;
ll n, m;
pai p[N];
vector<pai> G[N];
int a[N];
void dfs(int u, int fa) {
for (auto& [v, w] : G[u]) {
if (v == fa) continue;
a[v] = a[u] ^ w;
dfs(v, u);
}
}
vector<pai> seg;
int bit[40];
#define mid (l + r >> 1)
void build(int l, int r, int cnt, int L, int R, int val) {
if (L <= l && r <= R) {
int ql = (l ^ val) & bit[cnt];
int qr = ql + (1 << cnt) - 1;
seg.push_back({ ql,qr });
return;
}
if (L <= mid) build(l, mid, cnt - 1, L, R, val);
if (R > mid) build(mid + 1, r, cnt - 1, L, R, val);
}
int solve() {
bit[0] = (1 << 30) - 1;
for (int i = 1; i <= 30; ++i) {
bit[i] = bit[i - 1] ^ (1 << (i - 1));
}
n = read();
for (int i = 1; i <= n; ++i) p[i] = { read(), read() };
int u, v, w;
for (int i = 2; i <= n; ++i) {
u = read(), v = read(), w = read();
G[u].push_back({ v,w });
G[v].push_back({ u,w });
}
dfs(1, 0);
for (int i = 1; i <= n; ++i) {
build(0, bit[0], 30, p[i].first, p[i].second, a[i]);
}
vector<pai> all;
for (auto& [l, r] : seg) { // 扫描线
all.push_back({ l,1 });
all.push_back({ r + 1,-1 });
}
sort(all.begin(), all.end());
int res = 0, tag = 0;
for (int i = 0; i < all.size(); ++i) {
tag += all[i].second;
if (tag == n) {
res += all[i + 1].first - all[i].first;
}
}
print(res);
return 1;
} 
京公网安备 11010502036488号