题解 CF1824C 【LuoTianyi and XOR-Tree】
萌萌题。
题解
考虑随便树上一个节点
记
下面考虑转移。假设
此时出现了困难。因为花费最少次数让
那么就变成了这样一个过程:
- 先递归计算出
M_{v_1},M_{v_2},\cdots,M_{v_k} ; - 维护可重集
S=M_{v_1}\cup M_{v_2}\cup\cdots\cup M_{v_k} ; - 找到
S 中出现次数最多的那一些元素,记最多的出现次数为t ,那么f_u=(\sum f_{v_i})+k-t 。同时将这些出现次数最多的元素,异或上u 的权值,放到M_u 里。
如此操作,直到计算出
于是可以考虑启发式合并。先计算出
但是这时又有最后一个问题。我们需要把
上文提到的
实现细节有点多,可以看代码。
参考代码
#include<bits/stdc++.h>
#define up(l, r, i) for(int i = l, END##i = r;i <= END##i;++ i)
#define dn(r, l, i) for(int i = r, END##i = l;i >= END##i;-- i)
using namespace std;
typedef long long i64;
const int INF = 2147483647;
const int MAXN= 1e5 + 3;
vector <int> V[MAXN];
int G[MAXN], T[MAXN], K[MAXN], A[MAXN], F[MAXN], S[MAXN];
void dfs0(int u, int f){
S[u] = 1;
for(auto &v : V[u]) if(v != f){
dfs0(v, u), S[u] += S[v];
if(S[v] > S[G[u]]) G[u] = v;
}
}
map<int, bool> M[MAXN];
void dfs(int u, int f){
if(V[u].size() == 1 && f){
T[u] = u, M[T[u]][0] = true, K[u] = A[u], F[u] = 0;
} else {
map<int, int> S;
dfs(G[u], u);
T[u] = T[G[u]];
K[u] = K[G[u]];
int c = 1, sum = F[G[u]], w = 1;
for(auto &v : V[u]) if(v != f && v != G[u]){
dfs(v, u);
for(auto &x : M[T[v]]){
S[x.first ^ K[v]] ++;
}
++ c, sum += F[v];
}
for(auto &x : S){
if(M[T[u]].count(x.first ^ K[u])) ++ x.second;
w = max(w, x.second);
}
if(w == 1){
for(auto &x : S){
M[T[u]][x.first ^ K[u]] = true;
}
} else {
M[T[u]].clear();
for(auto &x : S){
if(x.second == w)
M[T[u]][x.first ^ K[u]] = true;
}
}
K[u] ^= A[u];
F[u] = sum + c - w;
}
}
int qread(){
int w = 1, c, ret;
while((c = getchar()) > '9' || c < '0') w = (c == '-' ? -1 : 1); ret = c - '0';
while((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + c - '0';
return ret * w;
}
int main(){
int n = qread();
up(1, n, i) A[i] = qread();
up(1, n - 1, i){
int u = qread(), v = qread();
V[u].push_back(v);
V[v].push_back(u);
}
dfs0(1, 0);
dfs(1, 0);
for(auto &x : M[T[1]]){
if((x.first ^ K[1]) == 0){
printf("%d\n", F[1]); exit(0);
}
}
printf("%d\n", F[1] + 1);
return 0;
}