NOIP十连测 涂色游戏

这是一道玄学组合数和神仙思路。。。

题目大意:给出一个n*m的网格,每个格子里只能涂一种颜色,一共有p中颜色,要求任意相邻两列都出现了

至少q种颜色的方案数。

n≤100,m≤10^{9},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个不同的东西放进去的方案数。

这个问题就是第二类斯特林,结论为g[i][j]=j*(g[i-1][j-1]+g[i-1][j]),大概的意思就是

可以从放过的盒子里在放一个,一共j种,也可以在没放过的新盒子放一个,这个新盒子可以是j中的任何一个,

所以一共j种。

那么最后得到的关于ans的表达式就是ans[j][k]=g[n][k]*\sum_{x=max(q,j,k)}^{min(p,j+k)}C_{j}^{j+k-x}C_{p-j}^{x-j}

对于dp的初值就是dp[1][j]=g[n][j]*C_{p}^{j},就是指从所有颜色中选出j个颜色的方案数乘上放j个颜色的方案数。

用矩阵乘法优化一下即可,最后答案是\sum_{i=1}^{p}f[n][i]。(PS:我懒了。。这个矩乘就直接用了)。。

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

 

 

你可能感兴趣的:(构造,dp,矩阵乘法)