• [HNOI2012]集合选数


    嘟嘟嘟


    这题真是一道好题。
    看到题中的限制,我就一直想dp,可是设出来的状态都无法转移,最后还是看题解了。
    题解真是妙,太妙了。
    就是我们构造一个矩阵,满足(a[i][j] = a[i][j - 1] * 2(j > 1), a[i][1] = a[i - 1][1] * 3)。这个矩阵最大不过(12 * 18),于是可以状压dp,每一次选择同一行中不选相邻的,两行之间不在同一列的转移。这个转移就很显然了。


    但是如果以(a[1][1] = 1)构造矩阵,有些数不会构造到,因此我们枚举所有的数,如果一个数不在矩阵中,就以他为左上角再构造个矩阵,这样答案就是每一次构造后dp的乘积了。


    这题稍微得卡个常,比如把不相邻的1的二进制数都预处理出来,然后把加法取模换成减模数(换完后贼块),就行了。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 1e5 + 5;
    const ll mod = 1e9 + 1;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n;
    bool vis[maxn];
    
    int s[19][1 << 19], len[19];
    In void init()
    {
      for(int i = 1; i < 19; ++i)
          for(int j = 0; j < (1 << i); ++j) if(!(j & (j << 1))) s[i][++len[i]] = j;
    }
    
    int f[13][19], lim[13], Lim;
    ll dp[13][1 << 19];
    In ll inc(ll a, ll b) {return a + b >= mod ? a + b - mod : a + b;}
    In ll Dp(int x)
    {
      for(int i = 1; i <= 12; ++i)
        {
          if(i == 1) f[i][1] = x;
          else f[i][1] = f[i - 1][1] * 3;
          if(f[i][1] > n) {Lim = i - 1; break;}
          vis[f[i][1]] = 1;
          for(int j = 2; j <= 18; ++j)
        {
          if((f[i][j - 1] << 1) > n) {lim[i] = j - 1; break;}
          vis[f[i][j] = (f[i][j - 1] << 1)] = 1;
        }
        }
      for(int i = 0; i < (1 << lim[1]); ++i) dp[1][i] = 0;
      for(int i = 1; i <= len[lim[1]]; ++i) dp[1][s[lim[1]][i]] = 1;
      for(int i = 2; i <= Lim; ++i)
        for(int j = 1; j <= len[lim[i]]; ++j)
          {
        dp[i][s[lim[i]][j]] = 0;
        for(int k = 1; k <= len[lim[i - 1]]; ++k)
          if(!(s[lim[i]][j] & s[lim[i - 1]][k]))
            dp[i][s[lim[i]][j]] = inc(dp[i][s[lim[i]][j]], dp[i - 1][s[lim[i - 1]][k]]);
          }
      ll ret = 0;
      for(int i = 1; i <= len[lim[Lim]]; ++i) ret = inc(ret, dp[Lim][s[lim[Lim]][i]]);
      return ret;
    }
    
    int main()
    {
      init();
      n = read();
      ll ans = 1;
      for(int i = 1; i <= n; ++i) if(!vis[i]) ans = ans * Dp(i) % mod;
      write(ans), enter;
      return 0;
    }
    
  • 相关阅读:
    学习心得——王梦茹
    优秀学生专栏——孙珩发
    优秀学生专栏——孙振涛
    学习心得——李嫣然、逯广捷
    Spring和Hibernate集成声明式事务 小强斋
    Hibernate——编程式事务 小强斋
    设计模式>原则 小强斋
    Spring>JDK动态代理和CGLIB字节码生成 小强斋
    Hibernate——编程式事务 小强斋
    设计模式>原则 小强斋
  • 原文地址:https://www.cnblogs.com/mrclr/p/10429892.html
Copyright © 2020-2023  润新知