• [COCI2019] Mobitel


    题目

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

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

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

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

    转移的话,我们直接除以下一个要走的格子的权值就好了,显然状态数不会超过(2sqrt{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
    ",(f[r][c]-ans+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    【JZOJ4743】积木【状压dp】
    学生增删改查练习
    Java关键字
    集合基本知识
    简单模拟用户登录
    遍历输入的字符串
    字符串截取
    equals与equalsIgnoreCase
    StringBuilder成员方法
    StringBuilder
  • 原文地址:https://www.cnblogs.com/asuldb/p/10967225.html
Copyright © 2020-2023  润新知