[COCI2019] Mobitel

题目

显然不小于\(n\)这个东西我们不是很好搞,考虑正难则反,求出有多少条路径小于\(n\),之后拿\(C_{n+m}^m\)一减就好了

于是状态为\(dp[i][j][k]\)表示到\((i,j)\)这个格子累计乘积为\(k\)的路径数,转移显然

但是一看就是过不了的级别

于是我们不存到现在的乘积是多少了,我们改成存从这个格子往下还能乘多大的数

转移的话,我们直接除以下一个要走的格子的权值就好了,显然状态数不会超过\(2\sqrt{n}\)

代码

#include<cstdio>
#include<cstring>
#define re register
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=1e9+7;
const int maxn=301;
int r,c,n,m,a[maxn][maxn];
int f[maxn][maxn],dp[2][maxn][2500],id[1000005],w[2500];
inline void add(int a,int b,int c,int x,int y,int z) {
    dp[a][b][c]=(dp[a][b][c]+dp[x][y][z])%mod;
}
int main() {
    r=read(),c=read(),n=read()-1;
    f[1][0]=1;
    for(re int i=1;i<=r;i++)
        for(re int j=1;j<=c;j++) f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
    for(re int l=1,r;l<=n;l=r+1) 
        r=n/(n/l),w[++m]=n/l,id[w[m]]=m;
    for(re int i=1;i<=r;i++)
        for(re int j=1;j<=c;j++) a[i][j]=read();
    dp[0][1][id[n/a[1][1]]]=1;
    int o=0;
    for(re int i=1;i<=r;i++,o^=1) {
        memset(dp[o^1],0,sizeof(dp[o^1]));
        for(re int j=1;j<=c;j++) 
            for(re int k=1;k<=m;k++) {
                if(!dp[o][j][k]) continue;
                if(i<r&&w[k]/a[i+1][j]>0) 
                    add(o^1,j,id[w[k]/a[i+1][j]],o,j,k);
                if(j<c&&w[k]/a[i][j+1]>0)
                    add(o,j+1,id[w[k]/a[i][j+1]],o,j,k);
            }
    }
    int ans=0;
    for(re int k=1;k<=m;k++) ans=(ans+dp[o^1][c][k])%mod;
    printf("%d\n",(f[r][c]-ans+mod)%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/asuldb/p/10967225.html