• [CQOI2012]局部极小值


    嘟嘟嘟


    谁说CQOI的题都是板儿题,我就觉得这题挺难的……


    看到数据范围这么小,就会想状压。然而(2 ^ {28})肯定过不了。不过对于所有的极小值的格子,最多不会超过8个,所以我们状压选了哪些局部极小值的格子(坑儿)。


    然后我们从小到大填数,那么对于一个数(i),他无非就两种填法:填入一个坑,或是填坑以外的点。
    填一个坑儿就是直接填,于是有(dp[i][S] = sum dp[i - 1][S) ^ ((1 << k)])
    不填坑的话,得考虑填上这个数之后,那些还没有填的坑仍能保证是坑,即不能往他们的四周填。因此我们预处理出来对于当前每一个状态,可以填的格子数目(num[S])。然后有转移方程(dp[i][S] = dp[i - 1][S] * (num[S] - (i - 1)))
    答案就是(dp[n * m][(1 << tot) - 1])(tot)是坑的总数。


    但光这样是不对的。因为一些不是坑的点填完后可能成为了坑,换句话说,我们上面的(dp[n * m][(1 << tot) -1])表示的是至少有(tot)个坑时的方案数,而我么需要的是恰好有这么多坑的方案数。
    那么就能想到容斥了!我们暴力枚举棋盘上哪些格子可以是坑,然后每构造一个棋盘,就dp一次,然后看看是加上还是减去。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<assert.h>
    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 ll mod = 12345678;
    In 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;
    }
    In void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
      freopen(".in", "r", stdin);
      freopen(".out", "w", stdout);
    #endif
    }
    
    int n, m, TOT;
    char s[5][8];
    ll ans = 0;
    
    In ll inc(ll a, ll b) {return a + b < mod ? a + b : a + b - mod;}
    
    struct Node
    {
      int x, y;
    }t[30];
    int tot = 0, num[(1 << 8) + 5];
    bool vis[5][8];
    ll dp[30][(1 << 8) + 5];
    const int dx[] = {-1, -1, 0, 1, 1, 1, 0, -1}, dy[] = {0, 1, 1, 1, 0, -1, -1, -1};
    In void calc()
    {
      tot = 0;
      for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
          if(s[i][j] == 'X') t[++tot] = (Node){i, j};
      for(int i = 0; i < (1 << tot); ++i)
        {
          Mem(vis, 0);
          for(int j = 1; j <= tot; ++j)
    	if(!((i >> (j - 1)) & 1))
    	  {
    	    vis[t[j].x][t[j].y] = 1;
    	    for(int k = 0; k < 8; ++k)
    	      {
    		int nx = t[j].x + dx[k], ny = t[j].y + dy[k];
    		if(nx && nx <= n && ny && ny <= m) vis[nx][ny] = 1;
    	      }
    	  }
          int cnt = 0;
          for(int j = 1; j <= n; ++j)
    	for(int k = 1; k <= m; ++k) cnt += vis[j][k];
          num[i] = n * m - cnt;
        }
      Mem(dp, 0); dp[0][0] = 1;
      for(int i = 1; i <= n * m; ++i)
        {
          for(int S = 0; S < (1 << tot); ++S)
    	{
    	  dp[i][S] = inc(dp[i][S], dp[i - 1][S] * (num[S] - i + 1) % mod);
    	  for(int j = 0; j < tot; ++j)
    	    if((S >> j) & 1) dp[i][S] = inc(dp[i][S], dp[i - 1][S ^ (1 << j)]);
    	}
        }
      ll tp = dp[n * m][(1 << tot) - 1];
      ans = inc(ans, ((tot - TOT) & 1) ? mod - tp: tp);
    }
    
    In void dfs(int x, int y)
    {
      if(x == n + 1) {calc(); return;}
      if(y == m) dfs(x + 1, 1);
      else dfs(x, y + 1);
      if(s[x][y] == 'X') return;
      bool flg = 1;
      for(int i = 0; i < 8 && flg; ++i)
        {
          int nx = x + dx[i], ny = y + dy[i];
          if(nx && nx <= n && ny && ny <= m)
    	if(s[nx][ny] == 'X') flg = 0;
        }
      if(!flg) return;
      s[x][y] = 'X';
      if(y == m) dfs(x + 1, 1);
      else dfs(x, y + 1);
      s[x][y] = '.';
    }
    
    int main()
    {
      //MYFILE();
      n = read(), m = read();
      for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
      for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j) TOT += (s[i][j] == 'X');
      dfs(1, 1);
      write(ans), enter;
      return 0;
    }
    
  • 相关阅读:
    ubuntu安装Sogou输入法失败
    二进制转换与此平台上的长模式不兼容
    thinkpad e570 如何进入bios
    计算beta分布并画图(1)
    python利用pandas和xlrd读取excel,特征筛选列
    python利用jieba进行中文分词去停用词
    python利用heapq实现小顶堆(查找最大的N个元素)
    python根据索引删除内容并写入文本
    [Water]UVA 11792 Commando War
    [最大子序列和]Hdu 5280 Senior's Array
  • 原文地址:https://www.cnblogs.com/mrclr/p/10943389.html
Copyright © 2020-2023  润新知