這是一道玄學組合數和神仙思路。。。
題目大意:給出一個n*m的網格,每個格子裏只能塗一種顏色,一共有p中顏色,要求任意相鄰兩列都出現了
至少q種顏色的方案數。
n≤100,m≤,q≤p≤100。
看這m的範圍,很容易想到矩陣乘法,所以可以先考慮遞推式。
設dp[i][j]表示前i列最後一列共有j種顏色的方案數。
那麼顯然可以得到dp[i][k]=dp[i-1][j]*ans[j][k]。其中ans[j][k]表示的是這層有j種顏色,下一層有k種。
那麼我們知道ans[j][k]怎麼求就可以得到答案了。
因爲兩次選擇的顏色會有重複,直接利用組合數尋找規律需要很多容斥,也很困難。
所以我們考慮枚舉兩次的並集,設並集爲x,那麼這次的方案組成就是在滿足條件的情況下,
和上次相交的顏色的選擇的方案乘上這次的新顏色的選擇的方案。j+k-x表示的就是交集。
最後還要乘上在n個位置塗上k中顏色的方案數。設g[n][k]表示這個方案數。
那麼就是要求在i個位置塗上j個顏色的方案數,可以類比爲有j個不同的盒子,需要把i個不同的東西放進去的方案數。
這個問題就是第二類斯特林,結論爲,大概的意思就是
可以從放過的盒子裏在放一個,一共j種,也可以在沒放過的新盒子放一個,這個新盒子可以是j中的任何一個,
所以一共j種。
那麼最後得到的關於ans的表達式就是
對於dp的初值就是,就是指從所有顏色中選出j個顏色的方案數乘上放j個顏色的方案數。
用矩陣乘法優化一下即可,最後答案是。(PS:我懶了。。這個矩乘就直接用了)。。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define mode 998244353
using namespace std;
typedef long long ll;
int n,m,p,q;
int val[105][105];
int used[2][105];
ll sum;
ll c[105][105];
ll g[105][105];
void yhsj()
{
for(int i=0;i<=102;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
{
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mode;
}
}
}
struct no
{
ll f[105][105];
}tmp,ans;
no operator *(no a,no b)
{
no re;
for(int i=1;i<=102;i++)
{
for(int j=1;j<=102;j++)
{
re.f[i][j]=0;
for(int k=1;k<=102;k++)
{
re.f[i][j]+=a.f[i][k]*b.f[k][j]%mode;
re.f[i][j]%=mode;
}
}
}
return re;
}
int main()
{
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&p,&q);
yhsj();
g[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
g[i][j]=j*(g[i-1][j]+g[i-1][j-1])%mode;
}
}
for(int j=1;j<=p;j++)//這層的顏色數
{
for(int k=1;k<=p;k++)//下層的顏色數
{
for(int x=max(q,max(j,k));x<=min(p,j+k);x++)
{
tmp.f[j][k]+=c[j][j+k-x]*c[p-j][x-j]%mode;
tmp.f[j][k]%=mode;
}
tmp.f[j][k]*=g[n][k]%mode;tmp.f[j][k]%=mode;
}
}
m--;
for(int i=1;i<=p;i++)ans.f[i][i]=1;
while(m)
{
if(m%2==1) ans=ans*tmp;
tmp=tmp*tmp;m>>=1;
}
for(int i=1;i<=p;i++)
{
for(int j=1;j<=p;j++)
{
sum=(sum+g[n][i]*ans.f[i][j]%mode*c[p][i]%mode)%mode;//相當於把初值的矩陣直接求了,真實的答案就是∑f[n][1-p]。
}
}
printf("%I64d\n",sum);
return 0;
}