zoukankan      html  css  js  c++  java
  • 【做题】TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp

    原文链接https://www.cnblogs.com/cly-none/p/9695526.html

    题意:求有多少对集合(S,T)满足:(S subseteq {1,2...n }, T subseteq {1,2...m},S igcap T = emptyset),且(S)中所有元素的异或和小于(T)中所有元素的异或和。对(10^9+7)取模。

    (n,m leq 2000)

    首先,通过记录当前两个集合的异或和,转移时考虑每个元素的3种选择,容易得到(O(n^3))的暴力dp。然而,要对此优化却是一件困难的事情。

    但无论如何,对状态的优化的必要的。因此,我们就必须避免同时记录两个集合的异或和。考虑两个异或和如果只有一位,那么它们的大小关系就能通过记录一位来得到。而对于多位的二进制数的大小比较,我们也只用比较不同的最高位就可以了。

    因此,我们枚举不同的最高位。那么,我们就可以忽略后面的位,并只用记录我们所枚举的这一位。剩下的问题就在于保证更高的位是相等的。那可以用记录两个数在更高位上的异或和实现,异或和为0,这两个数就是相等的。

    时间复杂度(O(n^2log n))

    upd18.9.25

    确实如zhouzhendong所说,这是(O(n^2))的。下面代码已修改。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 2060, MOD = (int)(1e9 + 7);
    int dp[N][N][2],n,m,len,ans;
    
    class WinterAndSnowmen {
    public:
      int getNumber( int N, int M );
    };
    int WinterAndSnowmen::getNumber(int N, int M) {
      n = N, m = M;
      len = max(n,m);
      for (int s = 1 ; s <= 12 ; ++ s) {
        memset(dp,0,sizeof dp);
        dp[0][0][0] = 1;
        for (register int i = 1 ; i <= len ; ++ i) {
          for (register int j = 0 ; j < (2048 >> (s-1)) ; ++ j) {
            for (int k = 0 ; k < 2 ; ++ k) {
              if (i <= n)
                (dp[i][j^(i>>(s-1))][k] += dp[i-1][j][k]) %= MOD;
              if (i <= m)
                (dp[i][j^(i>>(s-1))][k ^ ((i >> (s-1))&1)] += dp[i-1][j][k]) %= MOD;
              (dp[i][j][k] += dp[i-1][j][k]) %= MOD;
            }
          }
        }
        (ans += dp[len][1][1]) %= MOD;
      }
      return ans;
    }
    

    小结:虽然是“思维训练”中的题,但也会有自己没有掌握的技巧。

  • 相关阅读:
    列“XAxisBacklas”不属于表 Table。
    无经意中收看了《微软英雄》片段
    书编程(Asp.net)
    Runtime Error
    书编程(其他)
    书英语书
    【转】CodeBlocks中文版使用手册
    Java关键字this、super使用总结(转)
    分析对比主流Bootloader的性能
    C语言指针的初始化和赋值
  • 原文地址:https://www.cnblogs.com/cly-none/p/9695526.html
Copyright © 2011-2022 走看看