JZOJ4870. 【NOIP2016提高A组集训第9场11.7】涂色游戏

来源:互联网 发布:3m互助系统源码 编辑:程序博客网 时间:2024/05/16 06:30

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们设f[i][j]表示当前有i个格子恰好放了j种颜色的方案数,那么f[i][j]=f[i1][j1](p(j1))+f[i1][j]j。我们设g[j]表示n个格子恰好放了j种颜色的方案数,那么g[j]=f[n][j]。对于假如上一列放了j种颜色,这一列放k种颜色,两列颜色的并集为x,那么造成的方案数为f1[j][k]=g[j]Cjpmin(j+k,p)x=max(q,j,k)Cj+kxjCxjpj

这里写图片描述

解释一下:我们刚开始讲的g[j]包含的是p中任意j种颜色以任意顺序放入n个格子的方案数,那么对于p中任意j种颜色的方案数都是相等的,即g[j]Cjp。明显对于j,k,它们的并集x最大是j+k,但由于最多只有p种颜色,所以x的上限为minj+k,p,同时,x必须必j和k都大,由于题目要求交集必须大于q,所以x的下限为maxj,k,q。现在要求出k的颜色的组成,为了不重复不遗漏,我们对是否在j和k的交集的颜色分别求Cj+kxj表示j和k的交集有多少种情况(即在j中选出j和k的交集)。Cxjpj表示在k中但不在j中有多少种情况(因为在k中必在x中,并且从除j外的剩余p-j中颜色中选出)。

现在我们设f2[i][k]表示现在做到第i列,第i列放了j种颜色的方案数,那么f2[1][k]=g[k],f2[i][k]=pj=1f2[i1][k]f1[j][k]。打一下矩阵乘法即可,时间复杂度为O(logM1003)。

Code

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>#define ll long longusing namespace std;const ll maxn=105,mo=998244353;ll n,i,t,j,k,l,m,p,q,x,f[maxn][maxn],g[maxn],g1[maxn],c1[maxn];ll ans,d[maxn];ll mi(ll x,int y){    if (y==1) return x;    if (!y) return 1;    ll t=mi(x,y/2);    if (y%2) return t*t%mo*x%mo;return t*t%mo;}ll c(ll x,ll y){    return c1[x]*mi(c1[y]*c1[x-y]%mo,mo-2)%mo;}struct code{    ll a[maxn][maxn];    code friend operator * (code x,code y){        code z;memset(z.a,0,sizeof(z.a));int i,j,k;        for (i=1;i<maxn;i++)            for (j=1;j<maxn;j++)                for (k=1;k<maxn;k++)                    z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;        return z;    }}f1,f2,b;void mi1(int y){    while (y>1) d[++d[0]]=y%2,y/=2;    b=f1;    for (i=d[0];i>=1;i--){        b=b*b;        if (d[i]) b=b*f1;    }}int main(){    //freopen("data.in","r",stdin);//  freopen("color.in","r",stdin);freopen("color.out","w",stdout);    scanf("%d%d%d%d",&n,&m,&p,&q);    c1[0]=1;    for (i=1;i<maxn;i++)        c1[i]=c1[i-1]*i%mo;    f[0][0]=1;    for (i=1;i<=n;i++)        for (j=1;j<=min(i,p);j++)            f[i][j]=(f[i-1][j-1]*(p-(j-1))%mo+f[i-1][j]*j%mo)%mo;    for (j=1;j<=p;j++)        f2.a[1][j]=g[j]=f[n][j],g1[j]=(g[j]*mi(c(p,j),mo-2))%mo;    for (j=1;j<=p;j++)        for (k=1;k<=p;k++){            for (x=max(j,max(k,q));x<=min(j+k,p);x++)                f1.a[j][k]=(f1.a[j][k]+c(j,j+k-x)%mo*c(p-j,x-j)%mo)%mo;            f1.a[j][k]=f1.a[j][k]*g1[k]%mo;        }    if (m>1) mi1(m-1),f2=f2*b;    for (i=1;i<=p;i++)        ans=(ans+f2.a[1][i])%mo;    printf("%lld\n",ans);}
2 0
原创粉丝点击