LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数)
題目鏈接
https://loj.ac/problem/2731
題解
首先一個(gè)很自然的思路是,設(shè)\(dp[i][j]\)表示選了前\(i\)列,第\(2\)行第\(i\)列的格子是第\(j\)個(gè)被填上的。
還要加個(gè)第三維\(0/1\),表示第\(2\)行第\(i\)列不是/是這一列最后一個(gè)被填上的(這決定了它是被上下填上還是被左右填上)。
轉(zhuǎn)移: 若第\(2\)行第\(i\)列是棋子,則所有的都轉(zhuǎn)移到\(f[i][0][0]\).
(1) \(0\rightarrow 0\), 兩個(gè)互不影響,可以從任意的\(j'\)的\(f[i][j'][0]\)轉(zhuǎn)移過(guò)來(lái),組合數(shù)選出順序。
(2) \(1\rightarrow 0\), \((2,i)\)要在\((2,i-1)\)之前選,可以從大于當(dāng)前的\(j'\)轉(zhuǎn)移過(guò)來(lái),同樣用組合數(shù)選出順序。
(3) \(0\rightarrow 1\), \((2,i)\)要在\((2,i-1)\)之后選。但是這里要求\((2,i)\)早于\((1,i)\)和\((3,i)\)中的至少一個(gè),因此需要分類討論。我的代碼里第一個(gè)轉(zhuǎn)移是\((2,i)\)比上下兩個(gè)(如果存在)都晚,第二個(gè)轉(zhuǎn)移是\((2,i)\)早于上下兩個(gè)中的一個(gè)。
代碼
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair using namespace std;const int N = 2000; const int P = 1e9+7; char a[3][N+3]; llong dp[N+3][N*3+3][2]; llong fact[N*3+3],finv[N*3+3]; int n;llong quickpow(llong x,llong y) {llong cur = x,ret = 1ll;for(int i=0; y; i++){if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}cur = cur*cur%P;}return ret; }void updsum(llong &x,llong y) {x = (x+y)%P;}int main() {fact[0] = 1ll; for(int i=1; i<=N*3; i++) fact[i] = fact[i-1]*i%P;finv[N*3] = quickpow(fact[N*3],P-2); for(int i=N*3-1; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;scanf("%d",&n);for(int i=0; i<=2; i++) scanf("%s",a[i]+1);if(a[0][1]=='x'||a[0][n]=='x'||a[2][1]=='x'||a[2][n]=='x') {puts("0"); return 0;}for(int i=2; i<=n; i++) {if((a[0][i]=='x'&&a[0][i-1]=='x')||(a[2][i]=='x'&&a[2][i-1]=='x')) {puts("0"); return 0;}}int sum = a[1][1]=='x'?1:0;dp[1][sum][0] = 1ll;for(int i=2; i<=n; i++){for(int j=1; j<=sum; j++) {updsum(dp[i-1][j][0],dp[i-1][j-1][0]),updsum(dp[i-1][j][1],dp[i-1][j-1][1]);}int now = (a[0][i]=='x')+(a[2][i]=='x'); sum += now+(a[1][i]=='x');if(a[1][i]=='o'){dp[i][0][0] = (dp[i-1][sum-now][0]+dp[i-1][sum-now][1])*fact[sum]%P*finv[sum-now]%P;continue;}for(int j=1; j<=sum; j++){if(j-now-1>=0){updsum(dp[i][j][0],(dp[i-1][sum-now-1][1]-dp[i-1][j-now-1][1]+dp[i-1][sum-now-1][0]+P)*fact[j-1]%P*finv[j-now-1]%P);}if(now>=1){updsum(dp[i][j][1],dp[i-1][min(j-1,sum-now-1)][0]*fact[sum-j]%P*finv[sum-j-now]);if(now==2&&j>=2) {updsum(dp[i][j][1],dp[i-1][j-2][0]*(sum-j)%P*(j-1)*2ll%P);}} // printf("dp[%d][%d]=(%lld,%lld)\n",i,j,dp[i][j][0],dp[i][j][1]);}}llong ans = 0ll;for(int i=0; i<=sum; i++) ans = (ans+dp[n][i][0])%P;printf("%lld\n",ans);return 0; }總結(jié)
以上是生活随笔為你收集整理的LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: LOJ #2733 [JOI2016春季
- 下一篇: BZOJ 4221 [JOI2012春季