码迷,mamicode.com
首页 > 其他好文 > 详细

APC001F Xor Tree

时间:2020-02-29 23:57:23      阅读:120      评论:0      收藏:0      [点我收藏+]

标签:++   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]\)之间,也就是相同的数珂能会出现很多次。

窝们珂以这样划分:

  1. 对于每个数\(0\),单独划分为一个\(S\)
  2. 对于每两个相同的数,单独划分为一个\(S\)

这样下来就只剩下了每个数最多一次。

然后考虑\(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;
}

APC001F Xor Tree

标签:++   names   its   ati   else   一个   ret   子集   turn   

原文地址:https://www.cnblogs.com/libra9z/p/12387316.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!