Solution -「AT 3913」XOR Tree
Link.
给定一棵树,边 有边权 。每次操作可以使一条简单路径上的边权异或任意非负整数。求最少的操作次数使得所有边边权为 。
,。
好妙的题 www。
定义一个点的点权 为其所有邻接边边权的异或和,即 。一个至关重要的发现:所有边权为零等价于所有点权为零。
左推右是显然的;右推左,数归,考虑到叶子的边权等于点权,所以去掉所有叶子仍满足,得证。
再考虑一次操作,除路径两端的点,每个点有两条邻接边被异或了同一个数,所以这些点的点权不变!
非常 amazing 啊,这样一来问题就从树上剥离了——给一堆数,每次任选两个数异或同一个非负整数,求把这些数变成 的最小操作次数。
首先,若存在 ,显然应该用一次操作处理掉它们。问题进一步转化——给一个值域在 的集合(无重复元素),求把这些数变成 的最小操作次数。
鉴于 ,考虑状压。设 为处理集合 的最小操作次数。显然对于 内元素异或和不为 的 ,有 。接下来想想对于 的转移:
其中,前一项是暴力两两异或,后者即分别处理两个子集。
设 的上限 ,复杂度 。
#include <cstdio>
#include <cstring>
const int MAXN = 1e5, INF = 0x3f3f3f3f;
int n, val[MAXN + 5], cnt[16], f[1 << 16], xsum[1 << 16];
inline void chkmin ( int& a, const int b ) { if ( b < a ) a = b; }
int main () {
scanf ( "%d", &n );
for ( int i = 1, u, v, w; i < n; ++ i ) {
scanf ( "%d %d %d", &u, &v, &w );
val[u] ^= w, val[v] ^= w;
}
int ans = 0, S = 0;
for ( int i = 0; i < n; ++ i ) ++ cnt[val[i]];
for ( int i = 1; i < 16; ++ i ) {
S |= ( cnt[i] & 1 ) << i >> 1;
ans += cnt[i] >> 1;
}
for ( int i = 1; i < 1 << 15; ++ i ) {
for ( int j = 0; j < 15; ++ j ) {
if ( ( i >> j ) & 1 ) {
++ f[i], xsum[i] ^= j + 1;
}
}
-- f[i];
}
for ( int s = 0; s < 1 << 15; ++ s ) {
if ( xsum[s] ) continue;
for ( int t = s; ; t = ( t - 1 ) & s ) {
if ( ! xsum[t] && ! xsum[s ^ t] ) chkmin ( f[s], f[t] + f[s ^ t] );
if ( ! t ) break;
}
}
printf ( "%d\n", ans + f[S] );
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现