APC001F Xor Tree
Posted libra9z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了APC001F Xor Tree相关的知识,希望对你有一定的参考价值。
原题链接
这题是神仙思维题啊
设(a_u=igoplus_{ein ext{u.edges}} ext{weight}_e),即所有与(u)相邻的边的权值异或和。
那么,那个不寻常的操作即珂转化为:找两个下标(i,j)和一个数(x),要(a_i,a_j)同时异或上(x)。最后还是要求使得所有的(a_i=0)的最少操作数。
发现搞到这儿还是不会/kel
然后接着我们思考另一个东西:
设集合(S)满足(S)中的所有数的异或和为(0),且任意一个(S)的非空子集里的数的异或和都不为(0)。
那么窝们需要至少(|S|-1)次上面的那个操作才能使得(S)中的所有数变为(0)。
证明? 咕咕咕
算了,还是简单证明一下吧:
先看一个广义的推论:集合(|S|)至少需要经过(|S|-1)次操作才能使得(|S|)变成由(|S|-1)个(0)和一个(igoplus_S)组合出来的大小为(|S|)集合。
证明如下:
假设当(|S|=n-1)时推论成立。当(|S|=n)时,不妨设最后一次这样的操作中的一个数为(S_n)。那么前面窝们需要通过(n-2)次操作搞出来(n-2)个(0)和一个(igoplus_{1leq ileq n-1}S_i)。这即为符合假设。
所以若(|S|=n-1)时推论成立,则当(|S|=n)时推论成立。
又因为当(|S|=2),推论显然成立,所以上述推论成立。
当(igoplus_S=0)时,上述结论成立。
所以窝们要让操作数尽量少,就要划分出尽量多的(S)。
假设窝们划分出了(m)个(S),那么答案就是(n-m)。
然后窝们发现虽然会有(100000)个数,但是每个数都在范围([0,15])之间,也就是相同的数珂能会出现很多次。
窝们珂以这样划分:
- 对于每个数(0),单独划分为一个(S)
- 对于每两个相同的数,单独划分为一个(S)
这样下来就只剩下了每个数最多一次。
然后考虑(dp_{mask})表示出现数的集合是(mask)最多珂以划分为多少个(S)。
那么(dp_{mask} = max_{submaskin mask}{dp_{submask}+dp_{maskigoplus submask}})
总时间复杂度:(O(n+3^{15}))
贴一下代码:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
static const int Maxn = 100005;
int n;
int a[Maxn];
int dp[Maxn];
int main() {
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
++u, ++v;
a[u] ^= w, a[v] ^= w;
}
int ans = 0;
int allmask = 0;
for (int i = 1; i <= n; ++i) {
if (a[i] == 0) {
++ans;
}
else if (allmask & (1 << a[i] - 1)) {
allmask ^= (1 << a[i] - 1);
++ans;
}
else {
allmask ^= (1 << a[i] - 1);
}
}
dp[0] = 0;
for (int mask = 1; mask < (1 << 15); ++mask) {
dp[mask] = -1;
int xorsum = 0;
for (int i = 0; i < 15; ++i) {
if (mask >> i & 1) {
xorsum ^= (i + 1);
}
}
if (xorsum != 0) {
continue;
}
for (int smask = mask; smask; smask = (smask - 1) & mask) {
if (dp[smask] == -1) continue;
dp[mask] = max(dp[mask], dp[smask] + dp[mask ^ smask]);
}
dp[mask] = max(dp[mask], 1);
}
printf("%d
", n - (ans + dp[allmask]));
return 0;
}
以上是关于APC001F Xor Tree的主要内容,如果未能解决你的问题,请参考以下文章