[APC001F]

不妨采用一种常见的套路:我们把每个点的点权定义为它周围边的边权异或和。那么修改一条路径就转化为了修改两个点,最终的答案就是让所有点的点权都为 $0$。

我们难以找到直接贪心的策略。但 $0\leq a_i\leq 15$,考虑对于每种权值进行状压——先把权值相同的点两两消掉。在此之后,每种权值的点只可能出现一次。于是状压 $\text{DP}$ 的起始状态就确定了。

考虑转移,每次选择两个点,修改的权值必定是其中之一(否则肯定不优),且不管修改其中的哪个,最终剩下的都会是他俩的异或和。这样转移就能唯一确定了。

最终直接查询状态 $0$ 的值就行了。

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 1e5 + 10, S = 1 << 16;
int n, r, x, y, w, sum, a[N], f[S];
void cmin(int &x, int y){x = min(x, y);}
int main(){
    scanf("%d", &n);
    FL(i, 2, n){
        scanf("%d%d%d", &x, &y, &w);
        a[x + 1] ^= w, a[y + 1] ^= w;
    }
    FL(i, 1, n) if(a[i]) sum += (r >> a[i]) & 1, r ^= 1 << a[i];
    memset(f, 0x3f, sizeof(f));
    f[r] = sum;
    FR(s, r, 0) FL(i, 1, 15) if(s & (1 << i))
        FL(j, 1, 15) if(i != j && (s & (1 << j)))
            cmin(f[s ^ (1 << i) ^ (1 << j) ^ (1 << (i ^ j))], f[s] + 1 + ((s >> (i ^ j)) & 1));
    printf("%d\n", f[0]);
    return 0;
}
posted @ 2023-08-20 14:49  徐子洋  阅读(13)  评论(0)    收藏  举报  来源