日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数)

發布時間:2025/3/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接

https://loj.ac/problem/2731

題解

首先一個很自然的思路是,設\(dp[i][j]\)表示選了前\(i\)列,第\(2\)行第\(i\)列的格子是第\(j\)個被填上的。
還要加個第三維\(0/1\),表示第\(2\)行第\(i\)列不是/是這一列最后一個被填上的(這決定了它是被上下填上還是被左右填上)。
轉移: 若第\(2\)行第\(i\)列是棋子,則所有的都轉移到\(f[i][0][0]\).

(1) \(0\rightarrow 0\), 兩個互不影響,可以從任意的\(j'\)\(f[i][j'][0]\)轉移過來,組合數選出順序。

(2) \(1\rightarrow 0\), \((2,i)\)要在\((2,i-1)\)之前選,可以從大于當前的\(j'\)轉移過來,同樣用組合數選出順序。

(3) \(0\rightarrow 1\), \((2,i)\)要在\((2,i-1)\)之后選。但是這里要求\((2,i)\)早于\((1,i)\)\((3,i)\)中的至少一個,因此需要分類討論。我的代碼里第一個轉移是\((2,i)\)比上下兩個(如果存在)都晚,第二個轉移是\((2,i)\)早于上下兩個中的一個。

代碼

#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; }

總結

以上是生活随笔為你收集整理的LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。