标签:++ names its ati else 一个 ret 子集 turn
这题是神仙思维题啊
设\(a_u=\bigoplus_{e\in \text{u.edges}}\text{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\)和一个\(\bigoplus_S\)组合出来的大小为\(|S|\)集合。
证明如下:
假设当\(|S|=n-1\)时推论成立。当\(|S|=n\)时,不妨设最后一次这样的操作中的一个数为\(S_n\)。那么前面窝们需要通过\(n-2\)次操作搞出来\(n-2\)个\(0\)和一个\(\bigoplus_{1\leq i\leq n-1}S_i\)。这即为符合假设。
所以若\(|S|=n-1\)时推论成立,则当\(|S|=n\)时推论成立。
又因为当\(|S|=2\),推论显然成立,所以上述推论成立。
当\(\bigoplus_S=0\)时,上述结论成立。
所以窝们要让操作数尽量少,就要划分出尽量多的\(S\)。
假设窝们划分出了\(m\)个\(S\),那么答案就是\(n-m\)。
然后窝们发现虽然会有\(100000\)个数,但是每个数都在范围\([0,15]\)之间,也就是相同的数珂能会出现很多次。
窝们珂以这样划分:
这样下来就只剩下了每个数最多一次。
然后考虑\(dp_{mask}\)表示出现数的集合是\(mask\)最多珂以划分为多少个\(S\)。
那么\(dp_{mask} = \max_{submask\in mask}{dp_{submask}+dp_{mask\bigoplus 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", n - (ans + dp[allmask]));
return 0;
}
标签:++ names its ati else 一个 ret 子集 turn
原文地址:https://www.cnblogs.com/libra9z/p/12387316.html