• APC001F Xor Tree


    原题链接

    这题是神仙思维题啊

    \(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;
    }
    
    
  • 相关阅读:
    高性能分布式计算与存储系统设计概要
    .NET核心代码保护策略
    Web 通信 之 长连接、长轮询(long polling)
    C++数据结构之二叉查找树(BST)
    T4:T4 笔记 + Trait 示例
    腾讯2014软件开发
    CSS选择器从右向左的匹配规则
    Js面向对象编程
    Js杂谈-正则的测试与回溯次数
    Microsoft Message Analyzer (微软消息分析器,“网络抓包工具
  • 原文地址:https://www.cnblogs.com/libra9z/p/12387316.html
Copyright © 2020-2023  润新知