#2731. 「JOISC 2016 Day 1」棋盘游戏

1|0#2731. 「JOISC 2016 Day 1」棋盘游戏

1|1题目链接:

Link

1|2题目分析:

称之为神仙 dp 不为过。(至少我是什么认为的,可能是我太菜了 qwq

首先判断无解的情况,很显然无解有两个条件

  1. 四个角有空格
  2. 第一行和最后一行存在着连续两个空格

应该不用解释了吧。

然后就是这个题去分析一些性质了:

首先第一行和第三行的选择是相互独立的,因此我们只需要去 dp 中间一行的状态即可。

然后考虑如何设置这个 dp 状态,根据上面的分析,肯定是要按照列来进行转移的,然后我们可以设 dp[i][j] 表示前 i 列,当前选的是第 j 次的方案数。

但是考虑到当前列的转移是有两种情况的,因此我们还需要记一维表示当前是横着还是竖着进行转移的。所以当前的 dp 式子为 dp[i][j][0/1] 表示前 i 列,当前是第 j 个选的,当前选的情况是上下/左右转移。

考虑怎么进行转移。首先考虑中间已经确定的情况,那么此时就相当于不同的联通块了,那么直接把所有的状态都转移到下一位就可以了。

然后分两种情况:

  1. 当前是竖着进行转移,那么如果前面是横着进行转移的话,肯定是要在上一位的后面进行转移的。

    如果当前是竖着进行转移,上一位也是竖着进行转移的话,那么都是相互独立的,可以插到前面的状态里面。

  2. 如果当前是横着进行转移的话,第一种就是前面的竖着的必须要选上才行。

    第二种就是横着竖着都可以的情况,为了不重复计算,我们强制算在竖着选的里面,也就是都在后面进行选,那么剩下的情况就是上面的在前面选,剩下的在后面选,最后因为上下可以颠倒所以乘 2。

那么情况就讨论完了,因为是一个前缀的转移,所以我们利用前缀和进行优化即可。

1|3Code:

//editor : DRYAYST //Wo shi ge da SHA BI #include<bits/stdc++.h> #define g() getchar() #define il inline #define ull unsigned long long #define eps 1e-10 #define ll long long #define pa pair<int, int> #define for_1(i, n) for(int i = 1; i <= (n); ++i) #define for_0(i, n) for(int i = 0; i < (n); ++i) #define for_xy(i, x, y) for(int i = (x); i <= (y); ++i) #define for_yx(i, y, x) for(int i = (y); i >= (x); --i) #define for_edge(i, x) for(int i = head[x]; i; i = nxt[i]) #define int long long #define DB double #define ls (p<<1) #define rs (p<<1|1) #define m_p make_pair #define fi first #define se second using namespace std; const int N = 1e6 + 10, INF = 0x7f7f7f7f, mod = 1e9 + 7, M = 6000; il int qpow(int x, int k) {int ans = 1; while(k) {if(k & 1) ans = ans * x % mod; x = x * x % mod; k >>= 1; } return ans; } il int Add(int x, int y) {return (x += y) %= mod;} il int Del(int x, int y) {return (x = x - y + mod) % mod;} il int Mul(int x, int y) {return x * y % mod;} inline int re() { int x = 0, p = 1; char ch = getchar(); while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();} while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return x * p; } int jc[N], inv[N], f[2][6020][2]; char s[5][N]; int n, sum, now; il int C(int x, int y) {if(y > x) return 0; if(x == y || y == 0) return 1; return jc[x] * inv[y] % mod * inv[x - y] % mod; } signed main() { // freopen("game.in","r",stdin); freopen("game.out","w",stdout); jc[0] = jc[1] = 1, inv[0] = inv[1] = 1; for_1(i, M) jc[i] = jc[i - 1] * i % mod; inv[M] = qpow(jc[M], mod - 2); for(int i = M - 1; i >= 1; --i) inv[i] = (inv[i + 1] * (i + 1) % mod) ; n = re(); for_0(i, 3) scanf("%s", s[i] + 1); for_1(i, n) { if(s[0][i] == 'x' and (i==1 || i==n || s[0][i + 1] == 'x')) {cout<<0<<endl; return 0; } if(s[2][i] == 'x' and (i==1 || i==n || s[2][i + 1] == 'x')) {cout<<0<<endl; return 0; } }//判断无解的情况 if(s[1][1] == 'x') f[0][sum = 1][0] = 1; else f[0][0][0] = 1; for(int i = 2; i <= n; ++i) { now ^= 1; int pre = now ^ 1; memset(f[now], 0, sizeof(f[now])); int d = (s[0][i] == 'x') + (s[2][i] == 'x'); if(s[1][i] == 'o') { sum += d; f[now][0][0] = (f[pre][sum - d][1] + f[pre][sum - d][0]) % mod * C(sum, d) % mod * jc[d] % mod; //新的联通快 for(int j = 1; j <= sum; ++j) f[now][j][0] = f[now][0][0]; continue; } sum += (d + 1); for(int j = 1; j <= sum; ++j) { f[now][j][0] = (f[now][j][0] + ((f[pre][sum - d - 1][1] - f[pre][max(j - d - 1, 0LL)][1] + mod) * C(j - 1, d) % mod * jc[d] % mod)) % mod; //当前选的是竖着的,前面的是横着的,那么必须要比上一位更先选才行 f[now][j][0] = (f[now][j][0] + (f[pre][sum - d - 1][0] * C(j - 1, d) % mod * jc[d] % mod)) % mod;//当前是选的竖着的,前面也是竖着的,所以可以插到前面任意一个 if(i < n) { if(d >= 1) f[now][j][1] = (f[now][j][1] + f[pre][min(sum - d - 1, j - 1)][0] * C(sum - j, d) % mod * jc[d] % mod) % mod;//当前选的是横着的,那么前面竖着的必须要选上才行 if(d >= 2) f[now][j][1] = (f[now][j][1] + f[pre][max(0LL, j - 2)][0] * (sum - j) % mod * (j - 1) % mod * 2 % mod) % mod;//这里就是强制去选了,由于优先级的问题,只统计一次,这里是前面选上一部分后面选下一部分,两者颠倒 } } for_1(j, sum) { f[now][j][0] = (f[now][j][0] + f[now][j - 1][0]) % mod; f[now][j][1] = (f[now][j][1] + f[now][j - 1][1]) % mod; } } cout<<f[now][sum][0]<<endl; } /* 3 oxo xxo oxo */

__EOF__

本文作者Zwaire
本文链接https://www.cnblogs.com/Zwaire/p/16110627.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Zwaire  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示