计数

给定一个n*m的网格图,图上有一个起点和一个终点(起点和终点处没有障碍),以及若干障碍,对于每个数字0-9都将会给出一对数(ai,bi)。对于一个数t,我们从高位到低位扫一遍,对于t的每一个数字i将当前位置行加上ai列加上bi,若从起点开始在模拟过程中能不走出边界且不走到障碍最终到达终点,就说数t对于当前网格图合法。问区间[L,R]内合法的数t个数有多少,答案对1,000,000,007取模。
中途到达终点不算。移动是瞬间完成的。

输入格式:

第一行4个整数n、m、L、R,表示网格图的长、宽,询问区间的左、右边界。
接下来n行每行m个字符,对应了n*m的网格图, 表示没有障碍,*表示障碍,1表示起点,2表示终点,3表示起点和终点重合。
接下来10行每行2个整数ai,bi,按顺序分别对应数字0-9。

输出格式:

输出一个整数ans,表示合法的数字个数。

样例输入:

样例输出:

数据范围:

20%的数据1<=L<=R<=10000。
40%的数据1<=L<=R<=1,000,000,000且R-L<=1000000。
60%的数据1<=L<=R<=10^18。
另有10%的数据所有ai=bi=0。
100%的数据0<n,m<=50,|ai|<=n,|bi|<=m,1<=L<=R<=10^500且无前导0。

时间限制:

1s

空间限制:

256M

提示:

123符合要求。


  有点沙茶,写了暴力之后写了个10分的高精度。。。数位DP比高精度好写的多啊。。

f[pos][x][y][zero]代表倒数第pos步在x,y 前面有没有前导零。

然后直接记忆化无脑搜索就行了。 


#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
const int MOD=1000000007;
int n,m;
char map[55][55];
int dx[10],dy[10];
int sx,sy,tx,ty;
#define can(x,y) ((x<=n&&x>=1&&y<=m&&y>=1)&&map[x][y]!='*')
bool check(char *x){
	int len=strlen(x+1);
	int fuck=sx,shit=sy;
	rep(i,1,len){
		fuck+=dx[x[i]-'0'];
		shit+=dy[x[i]-'0'];
		if (!can(fuck,shit)) return false;
		}
	return fuck==tx&&shit==ty;
}
int f[505][55][55][2];int a[505];
int dfs(int pos,int x,int y,bool lim,bool zero){
	if (!can(x,y)) return 0;
	if (pos<=0) return !zero&&x==tx&&y==ty;
	if (!lim&&~f[pos][x][y][zero]) return f[pos][x][y][zero];
	int top=lim?a[pos]:9;int res=0;
	rep(i,0,top){
		if (i==0&&zero){(res+=dfs(pos-1,x,y,lim&&i==top,true))%=MOD;continue;}
		(res+=dfs(pos-1,x+dx[i],y+dy[i],lim&&i==top,false))%=MOD;
		}
	return lim?res:(f[pos][x][y][zero]=res);
}
int count(char *x){
	int len=strlen(x+1);
	rep(i,1,len) a[i]=x[len-i+1]-'0';
	return dfs(len,sx,sy,1,1);
}
char L[505],R[505];
int main(){
	memset(f,-1,sizeof f);
	scanf("%d%d",&n,&m);
	scanf("%s%s",L+1,R+1);
	rep(i,1,n) rep(j,1,m){
		char& c=map[i][j];
		for (c=getchar();c!='1'&&c!='2'&&c!='3'&&c!='*'&&c!=' ';c=getchar());
		if (c=='1') sx=i,sy=j;
		if (c=='2') tx=i,ty=j;
		if (c=='3') sx=tx=i,sy=ty=j;
		}
	rep(i,0,9) scanf("%d%d",&dx[i],&dy[i]);
	printf("%d\n",((count(R)-count(L)+check(L))%MOD+MOD)%MOD);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值