经过长时间的思考,我发现直接考虑对一条链进行修改是很难做出本题的,可能需要换一个方向。

可以发现本题中有操作的存在,是没有可以反过来做的做法的,因此正难则反这条路应该走不通。

那么唯一的办法就是简化这个操作或是转化问题了,不难发现前者应该是后者的基础,于是我们应该先将重心放在前者上。

可以发现本题是对一条链进行同一个操作,那么能否使用差分来使得多点操作变成单点操作呢?

不难发现由于差分的优秀性质是可以的,对于一次修改 \((x, y, k)\),我们只需要要在 \(x, y\) 处分别异或 \(k\),那么最终每条边的真实值就是这条边底下的点所在子树内修改的异或之和。

因为对于不在 \(x, y\) 链上的点,计算异或和时 \(x, y\) 会算两次相抵消。

但你很快就会发现一个问题,最终所有边全为 \(0\) 的条件是什么?

你会认为是所有点权为 \(0\),但事实上并非如此。

因为上述的差分事实上是将边权下方到点权的过程,那么根的权值应该是什么呢?

你会发现因为根上面没有边,所以这是一个未定义的问题,是不可以解决的。

那么只能再换一种思路了,不难发现我们我们的目的在于减少修改的点权,那么上面的过程暗示着我们要尽可能异或两次将中间的操作抵消。

不难发现的是,因为在一次链修改 \((x, y, k)\) 当中,除了 \(x, y\) 以外其他链上的点都连着恰好两条在链上的边。

那么我们只要定义每个点的点权包含这两点的异或和即可。

不难发现这个定义的点权要具有普适性,因此我们需要将每个点的点权定义为周边所有边的异或和。

那么这样每次操作只需修改两个点的点权即可。

你会发现上面的未定义问题也消失了,最终的状态不难发现就是所有点的点权为 \(0\)(从叶子开始向上归纳证明即可)。

那么现在的问题就转化成给定 \(n\) 个点权 \(a(a_i \le 15)\),每次操作选择两个点权异或上 \(x\) 求最小的操作次数使得所有点权变为 \(0\)。

那么你会发现每次选择两个相同的数直接异或这两个数是一定最优的,但这样也会产生一个问题:会不会经过这样一次操作后使得操作无解?

那么我们一定要思考的一个东西就是答案有解的充要条件。

直接寻找充分条件是不好找的,我们一般先寻找一些必要条件再组合成充要条件。

不难发现因为每次又是同时异或两个点,那么同样的总异或和还是不变的,那么一开始的总异或和为 \(0\) 是否是有解的充要条件呢?

不难发现是的,只需要每次选定两个点然后将其中一个消去即可。因为最终异或和为 \(0\),就一定不会剩下一个数落单的情况。

于是就可以发现不论我们进行什么操作,都是不会影响解的存在性的,那么就可放心地去寻找最优策略了。

回到最开始的想法,每次找到两个相同的权值直接同时消去显然是最优的。

那么这样一来最终我们剩下的点权就只有 \(16\) 个了。

还需要注意的是,\(0\) 是不需要消的。因为如果 \(0\) 和 \(a\) 消了一个 \(x\),再和 \(b\) 消回来,是等价于 \(a, b\) 消这个数的。

不难发现当点权没有重复时是不能直接贪心的,因为选择不一样的两个点会影响的状态也有所不同,那么只能考虑使用 \(dp\)。

因为点权非常少,可以直接考虑使用状压 \(dp\)。

很简单的,我们可以直接令 \(dp_S\) 为当前剩余数状态为 \(S\) 所需的最小操作次数。

那么每次转移需要枚举 \(S\) 中的两个点 \(i, j\),对 \(i, j\) 进行一次操作。

一种很直接的想法就是将 \(i, j\) 中的一个消去留下另一个,那么这样是否会更优呢?

不难发现确实是的,因为如果假设 \(a, b\) 异或上了 \(x\),最优情况下就是 \(S\) 中同时存在 \(c, d\) 使得 \(\rm a \ xor \ x = c, b \ xor \ x = d\)。

此时消去四个数也需要 \(3\) 次操作,不难发现每次至少消一个数也最多只要消 \(3\) 次。

于是每次转移到的状态就是 \(\rm S \ xor \ 2 ^ i \ xor \ 2 ^ j \ xor \ 2 ^ {i \ xor \ j}\),那么如果 \(S\) 中存在 \(\rm i \ xor \ j\),根据最开始的理论可知直接消去即可。

可以注意到的是每次转移到的状态总是比 \(S\) 小的,因此这个 \(dp\) 是有效的,从大往小转移即可。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define dep(i, l, r) for (int i = r; i >= l; --i)
const int N = 100000 + 5;
const int M = (1 << 16) + 5;
int n, u, v, w, ans, fir, a[N], dp[N], cnt[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int main() {
n = read(), memset(dp, 0x7f, sizeof(dp));
rep(i, 1, n - 1) u = read() + 1, v = read() + 1, w = read(), a[u] ^= w, a[v] ^= w;
rep(i, 1, n) ++cnt[a[i]];
rep(i, 1, 15) ans += cnt[i] / 2, cnt[i] %= 2;
rep(i, 1, 15) if(cnt[i]) fir += (1 << i);
dp[fir] = 0;
dep(S, 1, fir) if(dp[S] < n){
rep(i, 0, 15) if((1 << i) & S) {
rep(j, 0, 15) if((1 << j) & S) {
int val = 1, sta = S ^ (1 << i) ^ (1 << j) ^ (1 << (i ^ j));
if(S & (1 << (i ^ j))) ++val;
dp[sta] = min(dp[sta], dp[S] + val);
}
}
}
printf("%d", ans + dp[0]);
return 0;
}

AT3913 XOR Tree的更多相关文章

  1. AT3913 XOR Tree(巧妙转换+状压dp)

    Step1:首先定义一个点的权值为与其相连边的异或和.那么修改一条路径,权值改变的只有两个端点.边权都为0和点权都为0实质相同. Step2:那么现在和树的结构就没有什么关系了.每次选两个点,然后同时 ...

  2. [多校联考2019(Round 5 T1)] [ATCoder3912]Xor Tree(状压dp)

    [多校联考2019(Round 5)] [ATCoder3912]Xor Tree(状压dp) 题面 给出一棵n个点的树,每条边有边权v,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值 ...

  3. 「AGC035C」 Skolem XOR Tree

    「AGC035C」 Skolem XOR Tree 感觉有那么一点点上道了? 首先对于一个 \(n\),若 \(n\equiv 3 \pmod 4\),我们很快能够构造出一个合法解如 \(n,n-1, ...

  4. 【思维题 状压dp】APC001F - XOR Tree

    可能算是道中规中矩的套路题吧…… Time limit : 2sec / Memory limit : 256MB Problem Statement You are given a tree wit ...

  5. AtCoder - 3913 XOR Tree

    Problem Statement You are given a tree with N vertices. The vertices are numbered 0 through N−1, and ...

  6. Solution -「AT 3913」XOR Tree

    \(\mathcal{Description}\)   Link.   给定一棵树,边 \((u,v)\) 有边权 \(w(u,v)\).每次操作可以使一条简单路径上的边权异或任意非负整数.求最少的操 ...

  7. P1230: [Usaco2008 Nov]lites 开关灯

    嗯嗯,这是一道线段树的题,询问区间内亮着的灯的个数,我们可以把区间修改的线段树改一下,原本的求和改成若有奇数次更改则取反(总长度-亮着的灯个数),而判断是否奇数次只要数组加一个delta的值,upda ...

  8. Petrozavodsk Summer-2016. Ural FU Dandelion Contest

    A. Square Function 留坑. B. Guess by Remainder 询问$lcm(1,2,3,...,n)-1$即可一步猜出数. 计算$lcm$采用分治FFT即可,时间复杂度$O ...

  9. 【AtCoder】AtCoder Petrozavodsk Contest 001

    A - Two Integers 如果\(X\)是\(Y\)的倍数的话不存在 可以输出\(X \cdot (\frac{Y}{gcd(X,Y)} - 1)\) 代码 #include <bits ...

随机推荐

  1. Log4j 2.17.0 再曝漏洞,但不要惊慌!

    最新消息!根据Log4j官网发布,2.17.0版本还存在漏洞! 上图来自Log4j2官网:https://logging.apache.org/log4j/2.x/ 漏洞编号:CVE-2021-448 ...

  2. 「ARC096C」Everything on It

    Solution 容斥,钦定 \(i\) 个数 \(\leq 1\) 次. \[Ans=\sum_{i=0}^n (-1)^i\binom{n}{i}F(i) \] 其中 \(F(i)\) 表示有 \ ...

  3. 更新系统为High sierra 后无法使用Cocoapods

    sudo gem update --system sudo gem install -n /usr/local/bin cocoapods执行完就可以直接用了.

  4. Java初学者作业——输入一个五位数字,计算各位数字之和并输出,运行结果为五个数字之和(实践2)

    返回本章节 返回作业目录 需求说明: 编写Java程序,输入一个五位数字,计算各位数字之和并输出,运行结果为五个数字之和. 实现思路: (1)声明变量num,用于存储用户输入的数字. (2)通过Sca ...

  5. Qos 0/1/2的理解

    Qos 0/1/2的理解 Qos 0 最多一次的传输 消息是基于TCP/IP网络传输的.没有回应,在协议中也没有定义重传的语义.消息可能到达服务器1次,也可能根本不会到达. Qos 1 至少一次的传输 ...

  6. Zookeeper基础教程(一):认识Zookeeper

    引用百度百科的话 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服 ...

  7. CSS基础6之盒子模型1

    盒子概述 以下是盒子模型的一个图形解释 一.内边距(填充) 属性有: (1) padding  设置所有内边距 (2) padding-top  设置上边距 (3) padding-left 设置左边 ...

  8. XPTH定位总结

    xpath定位总结:nodename 选取此节点的所有子节点. / :从根节点选取.绝对定位 //:从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置. 相对定位(推荐使用相对定位) . :选取 ...

  9. Docker 部署 ElasticSearch-Head 及其他插件

    拉取ElasticSearch-Head镜像 docker pull mobz/elasticsearch-head:5 运行ElasticSearch-Head容器 docker run -d -- ...

  10. shell中的2>/dev/null

    1.文件描述符Linux系统预留可三个文件描述符:0.1和2,他们的意义如下所示:0--标准输入(stdin)1--标准输出(stdout)2--标准错误(stderr) 标准输出--stdout假设 ...