【2019/08/05测试 T3】凫趋雀跃

传送门


problem

有一个网格,每一步可以走 ( 0 … M x , 0 … M y ) (0…Mx,0…My) (0Mx,0My) 中任意非零向量。

K K K 种向量不能走,分别是 ( k i , k i (k_i,k_i (ki,ki),其中 k i k_i ki 10 10 10 的倍数。

求从 ( 0 , 0 ) (0,0) (0,0) R R R 步到 ( T x , T y ) (Tx,Ty) (Tx,Ty) 的方案数。对 1 0 4 + 7 10^4+7 104+7 取模。

数据范围: 1 ≤ T x , T y , M x , M y ≤ 800 1 ≤ Tx, Ty, Mx, My ≤ 800 1Tx,Ty,Mx,My800 1 ≤ R ≤ 1600 1 ≤ R ≤ 1600 1R1600 0 ≤ K ≤ 50 0 ≤ K ≤50 0K50 1 ≤ k i ≤ m i n ( M x , M y ) 1 ≤ k_i ≤ min(Mx, My) 1kimin(Mx,My),且 k i   m o d   10 = 0 k_i \bmod 10 = 0 kimod10=0


solution

算是一道比较考思维的题吧。

先不考虑非法向量, ( 0 , 0 ) (0,0) (0,0) 也先视为合法的。

我们用 f [ i ] [ x ] [ y ] f[i][x][y] f[i][x][y] 表示:走 i i i 步任意向量,走到 ( x , y ) (x, y) (x,y) 的方案数。

但是这样显然是会炸空间的,我们不妨换种表示方式,即用 f x [ i ] [ j ] f_x[i][j] fx[i][j] 表示走 i i i 步走到横坐标 ≤ j \le j j 的方案数, f y [ i ] [ j ] f_y[i][j] fy[i][j] 同理。

考虑转移,用 f x [ i ] [ j ] f_x[i][j] fx[i][j] 的前缀和即可快速转移,方程如下( f y f_y fy 计算同理):

f x [ i ] [ j ] = f x [ i ] [ j − 1 ] + f x [ i − 1 ] [ j ] − f x [ i − 1 ] [ j − M x − 1 ] f_x[i][j]=f_x[i][j-1]+f_x[i-1][j]-f_x[i-1][j-Mx-1] fx[i][j]=fx[i][j1]+fx[i1][j]fx[i1][jMx1]

注意一下边界。那么显然有 f [ i ] [ x ] [ y ] = f x [ i ] [ x ] × f y [ i ] [ y ] f[i][x][y]=f_x[i][x]\times f_y[i][y] f[i][x][y]=fx[i][x]×fy[i][y]

现在考虑如何减掉非法向量。

我们用 g [ i ] [ x ] g[i][x] g[i][x] 表示:走 i i i 步非法的向量,走到 ( 10 x , 10 x ) (10x, 10x) (10x,10x) 的方案数。

显然有转移:

g [ i + 1 ] [ x + k j ] ← g [ i ] [ x ] g[i+1][x+k_j]\leftarrow g[i][x] g[i+1][x+kj]g[i][x]

然后我们用容斥的思想,最后的答案就是:

a n s = ∑ i = 0 R ∑ j = 0 ⌊ m i n ( T x , T y ) 10 ⌋ ( − 1 ) i ( i R ) g [ i ] [ j ] × f [ R − i ] [ T x − 10 j ] [ T y − 10 j ] ans=\sum_{i=0}^R\sum_{j=0}^{\lfloor \frac{min(Tx,Ty)}{10}\rfloor} (-1)^i(^R_i)g[i][j]\times f[R-i][Tx-10j][Ty-10j] ans=i=0Rj=010min(Tx,Ty)(1)i(iR)g[i][j]×f[Ri][Tx10j][Ty10j]


code

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
const int X=805,R=1605,P=1e4+7;
int Tx,Ty,Mx,My,r,K;
int fac[R],inv[R],fx[R][X],fy[R][X],g[R][X/10];
vector<int>ban;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b,int ans=1){
	for(;b;b>>=1,a=mul(a,a))
		if(b&1)  ans=mul(ans,a);
	return ans;
}
void prework(){
	fac[0]=fac[1]=1;
	for(int i=2;i<R;++i)  fac[i]=mul(fac[i-1],i);
	inv[R-1]=power(fac[R-1],P-2);
	for(int i=R-2;~i;--i)  inv[i]=mul(inv[i+1],i+1);
}
int C(int n,int m)  {return mul(mul(inv[m],inv[n-m]),fac[n]);}
int F(int r,int x,int y)  {return mul(dec(fx[r][x+1],fx[r][x]),dec(fy[r][y+1],fy[r][y]));}
int solve(){
	prework();
	int top=min(Tx,Ty)/10;
	g[0][0]=1;
	for(int i=1;i<=r;++i){
		for(int j=0;j<ban.size();++j){
			int x=ban[j]/10;
			for(int k=x;k<=top;++k)  g[i][k]=add(g[i][k],g[i-1][k-x]);
		}
	}
	for(int i=1;i<=Tx+1;++i)  fx[0][i]=1;
	for(int i=1;i<=r;++i)
		for(int j=1;j<=Tx+1;++j)
			fx[i][j]=dec(add(fx[i][j-1],fx[i-1][j]),fx[i-1][j-min(j-1,Mx)-1]);
	for(int i=1;i<=Ty+1;++i)  fy[0][i]=1;
	for(int i=1;i<=r;++i)
		for(int j=1;j<=Ty+1;++j)
			fy[i][j]=dec(add(fy[i][j-1],fy[i-1][j]),fy[i-1][j-min(j-1,My)-1]);
	int ans=0;
	for(int i=0;i<=r;++i){
		int now=(i&1)?(P-C(r,i)):C(r,i);
		for(int j=0;j<=top;++j)
			ans=add(ans,mul(mul(now,g[i][j]),F(r-i,Tx-10*j,Ty-10*j)));
	}
	return ans;
}
int main(){
	scanf("%d%d%d%d%d%d",&Tx,&Ty,&Mx,&My,&r,&K);
	for(int i=1,x;i<=K;++i)  scanf("%d",&x),ban.push_back(x);
	ban.push_back(0);
	printf("%d\n",solve());
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值