[SCOI2008]着色方案
-
-
題目描述
有n個木塊排成一行,從左到右依次編號爲1~n。你有k種顏色的油漆,其
中第i 種顏色的油漆足夠塗ci 個木塊。所有油漆剛好足夠塗滿所有木塊,即
c1+c2+...+ck=n。相鄰兩個木塊塗相同色顯得很難看,所以你希望統計任意兩個相
鄰木塊顏色不同的着色方案。
輸入輸出格式
輸入格式:第一行爲一個正整數k,第二行包含k個整數c1, c2, ... , ck。
輸出一個整數,即方案總數模1,000,000,007的結果。
輸入輸出樣例
輸入樣例#1: 複製
3 1 2 3
輸出樣例#1: 複製
10
我們設表示使用了前i種油漆,有j個相鄰的同色塊的方案數。
-
考慮新加一種油漆i+1。我們假設有x個,我們先將他們分組。設分成了q組,那麼很顯然有種方案,並且會新產生x-q個相鄰的顏色塊。我們還要將分好的組插入原來的木塊中間(包括兩邊),顯然有個位置,其中有j個位置左右的顏色相同。我們在枚舉這q組中的k個(這一步有有種方案)插入左右 顏色相同的位置(這一步有中方案),其它的插入左右亞瑟不同的位置。插入過後顏色相同的數量變成了。將前面的所有組合數乘起來,轉移方程就是。
-
開始想到的是同種
顏色的油漆一個一個地加入,但是在加入的時候還要考慮相同顏色與不同顏色的塊要分開考慮,最後還要去重。。。就是各種複雜的操作,最後還是沒調出來。
-
所以有些題適合逐個DP,有些適合一組一組地DP。
-
-
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #include<map> #include<vector> #include<ctime> #define ll long long #define N 20 #define mod 1000000007 using namespace std; inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n; int sum[N],num[N]; ll c[80][80],f[N][80]; int main() { c[0][0]=1; for(int i=1;i<=75;i++) for(int j=0;j<=i;j++) c[i][j]=(!j||j==i)?1:(c[i-1][j-1]+c[i-1][j])%mod; n=Get(); for(int i=1;i<=n;i++) { num[i]=Get(); sum[i]=num[i]+sum[i-1]; } f[1][num[1]-1]=1; for(int i=1;i<n;i++) { for(int j=0;j<=75;j++) { if(!f[i][j]) continue ; for(int q=1;q<=num[i+1];q++) {//分成q組 if(q>sum[i]+1) continue ; for(int k=0;k<=q;k++) {//其中k組放在相同塊中 if(k>j||q-k>sum[i]+1-j||j+num[i+1]-q-k<0) continue ; (f[i+1][j+num[i+1]-q-k]+=f[i][j]*c[num[i+1]-1][q-1]%mod*c[j][k]%mod*c[sum[i]+1-j][q-k])%=mod; } } } } cout<<f[n][0]; return 0; }