[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;
}
posted @ 2019-06-03 14:03  asuldb  阅读(401)  评论(0编辑  收藏  举报