Love Live! - 牛客
题意
一颗\(n(2\le n \le10^5)\)个结点的树,树上的每条边有一个边权\(w(1\le w < n)\),且所有边的边权是互不相同的。定义一条简单路径的\(beautiful\)为该路径上所有边的异或和,定义一条简单路径的\(diffculty\)为该路径上所有边的边权最大值。求对于\([1,n)\)的每种\(diffculty\),所对应的最大\(beautiful\)。
题解
假设根节点为1号结点,一条简单路径\(u -u_1-u_2-u_3-\dots -v\),记该简单路径上所有边权的异或和为\(sum_{(u,v)}\),那么\(sum_{(u,v)} = sum_{(u,1)}\bigoplus sum_{(v,1)}\)。因为简单路径的困难度是该路径上的最大边权,所以先将树边按照从小到大排序,然后在加边的同时用并查集维护联通块。再对每个连通块开一个\(01-tire\)树以求解“对于给定的\(x\),在集合中找一个数字\(y\),使得\(x\bigoplus y\)最大的问题”。最后每加一条边,就启发式合并这条边连接的两个连通块并记录答案。\(O(nlog^2n)\)
收获
启发式合并(树的合并和剖分)
简单路径的异或和可以拆分成两端点到根结点的异或和的异或和
代码
const int N = 100005;
struct Edge {
int u, v, next, w;
Edge() {}
Edge(int u, int v, int w) : u(u), v(v), w(w) {}
Edge(int u, int v, int w, int next) : u(u), v(v), w(w), next(next) {}
bool operator < (const Edge &r) const {
return w < r.w;
}
}e[N << 1], G[N];
struct Tire {
int tot;
int T[N], a[N * 200][2];
void Inite(int cnt) {
tot = cnt + 1;
mem(a, 0);
}
void Insert(int root, int x) {
bitset<20> b(x);
for (int i = 19; ~i; --i) {
if (!a[root][b[i]]) a[root][b[i]] = ++tot;
root = a[root][b[i]];
}
}
int Query(int root, int x) {
bitset<20> b(x);
int res = 0;
for (int i = 19; ~i; --i) {
int id = b[i] ^ 1;
bool flag = true;
if (!a[root][id]) {
flag = false;
id = id ^ 1;
}
if (flag) res += (1 << i);
root = a[root][id];
}
return res;
}
}tire;
int n, tot;
int head[N], prefix[N], pa[N], sz[N];
vector<int> v[N];
void Inite() {
tire.Inite(n);
mem(head, -1);
prefix[1] = tot = 0;
}
void addedge(int u, int v, int w) {
e[++tot] = Edge(u, v, w, head[u]);
head[u] = tot;
}
void DFS(int u, int p) {
for (int i = head[u]; ~i; i = e[i].next) if (e[i].v != p) {
int v = e[i].v;
prefix[v] = prefix[u] ^ e[i].w;
DFS(v, u);
}
}
int Find(int a) {
return (a == pa[a] ? a : pa[a] = Find(pa[a]));
}
int main()
{
sc(n);
Inite();
rep(i, 1, n) {
int u, v, w;
sc(u), sc(v), sc(w);
addedge(u, v, w);
addedge(v, u, w);
G[i] = Edge(u, v, w);
}
DFS(1, 1);
Rep(i, 1, n) {
pa[i] = i;
sz[i] = 1;
tire.T[i] = i;
tire.Insert(i, prefix[i]);
v[i].pb(prefix[i]);
}
sort(G + 1, G + n);
rep(i, 1, n) {
int pu = Find(G[i].u), pv = Find(G[i].v);
if (sz[pu] > sz[pv]) swap(pu, pv);
int ans = 0;
for (auto it : v[pu]) ans = max(ans, tire.Query(tire.T[pv], it));
for (auto it : v[pu]) {
tire.Insert(tire.T[pv], it);
v[pv].pb(it);
}
pa[pu] = pv;
sz[pv] = sz[pv] + sz[pu];
printf("%d%c", ans, (i + 1 == n ? '\n' : ' '));
}
return 0;
}
ps
还是有几处不明白,启发式合并的复杂度是怎么算的,这棵树的结点该怎么估算。