jzoj 4870. 【NOIP2016提高A组集训第9场11.7】涂色游戏 动态规划+排列组合+矩阵乘法

来源:互联网 发布:qt for windows 编辑:程序博客网 时间:2024/05/16 06:19

题目

有n行m列的矩阵和p种颜色,要求在每个格子内填上一种颜色,使得每两列的颜色种类不少于q。求满足条件的方案数。
n<=100,m<=109,q<=p<=100

分析

这也是noip十连测忘了第几测的t2,当场没做出来,现在回想起来好像也并不是当初想象的那样难。

这题我至今没有在jzoj上面AC,因为有几个点在本地上跑过了,在上面却跑挂了。BPM说是因为我的某些写法不优美,所以就假装AC好了(逃)。

我们首先预处理一些东西,g[i,j]表示把i种颜色涂到j个格子里面,每种颜色至少出现一次的方案数。
g[i,j]=g[i-1][j-1]*i+g[i][j-1]*i
C[i,j]表示组合数,C[i,j]=C[i-1,j]+C[i-1,j-1]
那么现在就要开始对答案进行递推啦。
设f[i,j]表示有i列且第i列涂了j种颜色的方案数,那么我们再枚举一个k表示上一列的颜色数量,l表示在第i列涂多少种不同于上一列任何一种颜色的颜色,那么可以得到
f[i,j]=pk=1jl=qkf[i1,k]C[pk,l]C[k,jl]g[j,n]
那么对于满分做法我们只要在这个基础上加上矩阵优化就好了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define ll long long#define N 105#define MOD 998244353using namespace std;int n,m,p,q,C[N][N],g[N][N];struct arr{int a[N][N];}a,b,c;arr mul(arr a,arr b){    arr c;    memset(c.a,0,sizeof(c.a));    for (int i=0;i<=p;i++)        for (int j=0;j<=p;j++)            for (int k=0;k<=p;k++)                c.a[i][j]=(c.a[i][j]+(ll)a.a[i][k]*b.a[k][j]%MOD)%MOD;    return c;}void ksm(int x){    while (x)    {        if (x%2==1) c=mul(a,c);        a=mul(a,a);        x/=2;    }}int main(){    freopen("C.in","r",stdin);freopen("test.out","w",stdout);    scanf("%d%d%d%d",&n,&m,&p,&q);    C[0][0]=1;    for (int i=1;i<=p;i++)    {        C[i][0]=1;        for (int j=1;j<=i;j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;    }    for (int i=1;i<=n;i++)        g[1][i]=1;    for (int i=2;i<=p;i++)        for (int j=i;j<=n;j++)            g[i][j]=((ll)g[i-1][j-1]*i%MOD+(ll)g[i][j-1]*i%MOD)%MOD;    for (int j=0;j<=p;j++)        for (int k=0;k<=p;k++)            for (int l=q-k;l<=j;l++)                a.a[k][j]=(a.a[k][j]+(ll)C[p-k][l]*C[k][j-l]%MOD*g[j][n]%MOD)%MOD;    for (int i=0;i<=p;i++)        c.a[i][i]=1;    ksm(m);    int ans=0;    for (int i=0;i<=p;i++)        ans=(ans+c.a[p][i])%MOD;    printf("%d",ans);    return 0;}
0 0
原创粉丝点击