CQOI2011 放棋子 題解 dp 組合數學

2022-03-16 16:57:29 字數 1731 閱讀 3401

輸入第一行為兩個整數n, m, c,即行數、列數和棋子的顏色數。

第二行包含c個正整數,即每個顏色的棋子數。

所有顏色的棋子總數保證不超過nm。

n,m<=30 c<=10 總棋子數有大於250的情況。

輸出僅一行,即方案總數除以 1,000,000,009的餘數。

4 2 2

3 18

$solution$

20%:爆搜,沒甚麼技術含量雖然我考場上還是沒打對只騙到10分orz

100%:

考慮dp

設$f[i][j][k]$為前k種顏色的棋子佔任意i行j列的方案數

那麼這個值肯定是前面一系列值的$\sum$

顯然需要列舉兩層$0<=l

之後就可以得到$f[l][r][k-1]$並將其累加

但因為我們設的狀態是任意行列

需要在剩下的$n-l$行中選$i-l$行,列的話同理

所以要$*c_^*c_^$,

而且如果要轉移過去還必須乘上某一種顏色佔任意i行j列的方案數

這時設$g[i][j][k]$表示k枚同色棋子佔任意i行j列的方案數

可得:$f[i][j][k] = \sum _ ^ \sum _ ^ f[l][r][k - 1] * g[i - l][j - r][a[k]] * c_ ^ * c_ ^ $

正向求g比較困難,我們可以逆向思維,用所有方案數-不合法方案數之和

$g[i][j][k] = c_ ^ - \sum _ ^ \sum _ ^ g[l][r][k] * c_^ * c_ ^ $

最後統計$ans=\sum _ ^ \sum _ ^ f[i][j][c]$

收穫:如果覺得狀態設計得當,而缺少轉移方程的某一部分時,不妨設乙個輔助陣列單獨考慮。

#include#include

using

namespace

std;

typedef

long

long

ll;int n,m,c,a[35

];const ll mod=1e9+9

;ll f[

35][35][15],g[35][35][920],ans=0,c[920][920

];int

main()

f[0][0][0]=1;c[0][0]=1

;

for(int i=1;i<=n*m;i++)

for(int k=1;k<=c;k++)

for(int i=1;i<=n;i++)

for(int j=1;j<=m;j++)

for(int k=1;k<=c;k++)

for(int i=1;i<=n;i++)

for(int j=1;j<=m;j++)

for(int l=0;l)

for(int r=0;r)

(f[i][j][k]+=c[n-l][i-l]*c[m-r][j-r]%mod*f[l][r][k-1]%mod*g[i-l][j-r][a[k]]%mod)%=mod;

for(int i=1;i<=n;i++)

for(int j=1;j<=m;j++)

(ans+=f[i][j][c])%=mod;

cout

return0;

}

洛谷P3158 CQOI2011 放棋子

題目描述 題解 考慮 text f i j k 表示用前 k 種棋子占領了 i 行 j 列的方案數 考慮 f 的轉移 f i j k sum sum f l r k 1 times times times g i l j l a k 其中 g i j k 表示用 k 個同種顏色的棋子占領 i 行 j...

Cqoi2011 動態逆序對

主席樹套樹狀陣列。主席樹第一題。鏈結靜態的逆序對問題很簡單,用線段樹或者是樹狀陣列即可解決。現在的問題是如何解決一道動態的逆序對問題?我們先把所有的逆序對統計出來。每次刪除數,我們可以把這個數對於逆序對個數的貢獻刪除出去。這個貢獻如何統計呢?front i 記錄i位置之前有多少個數比這個數大 bac...

CQOI2011 動態逆序對

這是一道cdq分治的好題,這道題的前置知識是cdq分治解決三維偏序問題,如果不會這個話請先自行學習。首先第乙個答案很顯然就是逆序對的數量,然後後面每次的刪除操作,我們考慮把這個被刪除的點原先的貢獻從答案中拿掉。我們用y表示這個點的數,del表示第幾個被刪除,若沒有被刪除則del m 1 考慮每個點的...