AHOI2009 中国象棋
这题感觉还是与什么炮兵阵地之类的很相似的……不过首先看一眼数据范围,100?这怎么状压都状压不下,再者,一个炮要考虑一整行/列,直接暴力枚举肯定做不到。
那怎么办?首先很显然每行/列最多只能放两个炮。之后因为我们这道题首先不会统计你一共放多少个炮,二来统计的是方案数……看过dalao的题解之后,我们突然领悟到其实我们是不需要考虑当前棋盘上具体状态是什么样的。(因为要求方案数,必定枚举到所有状态)
我们按每一行来处理,因为一行最多能放两个炮,所以就有了以下的放法。
1.不放炮
2.放一个炮,放置在原来没有炮的一列
3.放一个炮,放置在原来有炮的一列
4.放两个炮,分别放在两个原来的空列
5.放两个炮,分别放在一个空列和一个有一个炮的一列
6.放两个炮,分别放在两个有一个炮的空列
当时自己特别智障,还写了一个把两个炮放在同一列的转移方程(你一行之内怎么能把两个炮放在一列)
既然如此,我们就用dp[i][j][k]表示当前放炮到第i行,其中一共有j列有一个炮,k列有两个炮的情况数。
那么从上到下对应上面六种情况的DP方程就是:
if(dp[i-1][j][k]) { dp[i][j][k] += dp[i-1][j][k],dp[i][j][k] %= mod; if(m-k-j > 0) dp[i][j+1][k] += dp[i-1][j][k] * (m-k-j),dp[i][j+1][k] %= mod; if(j > 0) dp[i][j-1][k+1] += dp[i-1][j][k] * j,dp[i][j-1][k+1] %= mod; if(m-k-j > 0 && j > 0) dp[i][j][k+1] += dp[i-1][j][k] * (m-j-k) * j,dp[i][j][k+1] %= mod; if(j > 1) dp[i][j-2][k+2] += dp[i-1][j][k] * c(j),dp[i][j-2][k+2] %= mod; if(m-k-j > 0) dp[i][j+2][k] += dp[i-1][j][k] * c(m-k-j),dp[i][j+2][k] %= mod; }
其中C(x)相当于C(x,2).这样就可以做了。最后的答案是sigma{dp[n][i][j]}.
注意初始的时候dp[0][0][0] = 1。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 50005; const int mod = 9999973; ll n,m,dp[105][105][105],ans; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } ll c(ll x) { return x * (x-1) / 2; } int main() { n = read(),m = read(); dp[0][0][0] = 1; rep(i,1,n) { rep(j,0,m) rep(k,0,m-j) { if(dp[i-1][j][k]) { dp[i][j][k] += dp[i-1][j][k],dp[i][j][k] %= mod; if(m-k-j > 0) dp[i][j+1][k] += dp[i-1][j][k] * (m-k-j),dp[i][j+1][k] %= mod; if(j > 0) dp[i][j-1][k+1] += dp[i-1][j][k] * j,dp[i][j-1][k+1] %= mod; //if(m-k-j > 0) dp[i+1][j][k+1] += dp[i][j][k] * (m-k-j),dp[i+1][j][k+1] %= mod; if(m-k-j > 0 && j > 0) dp[i][j][k+1] += dp[i-1][j][k] * (m-j-k) * j,dp[i][j][k+1] %= mod; if(j > 1) dp[i][j-2][k+2] += dp[i-1][j][k] * c(j),dp[i][j-2][k+2] %= mod; if(m-k-j > 0) dp[i][j+2][k] += dp[i-1][j][k] * c(m-k-j),dp[i][j+2][k] %= mod; } } } rep(i,0,m) rep(j,0,m-i) ans += dp[n][i][j],ans %= mod; printf("%lld\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。