【TC SRM 498 DIV1 Hard】【TC 11223】FoxJumping(容斥)(DP)

传送门


题解:

f [ i ] [ x ] [ y ] f[i][x][y] f[i][x][y]表示用 i i i个任意向量走到 ( x , y ) (x,y) (x,y)的方案数, g [ i ] [ k ] g[i][k] g[i][k]表示用 i i i个非法向量走到 k ∗ 10 , k ∗ 10 k*10,k*10 k10,k10的方案数。

DP发现, f [ i ] [ x ] [ y ] f[i][x][y] f[i][x][y] x x x y y y的两维没有关系,分开DP,前缀和优化一下。

感觉直接并不好处理,禁用 0 0 0,允许少于 R R R步。

然后随便枚举非法步数容斥一下就行了,实际上就是算了个前缀和。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int mod=1e4+7;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b){int res=1;
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}

class FoxJumping{
	private:
		static cs int R=1.6e3+9,M=857;
		
		int fx[R][M],fy[R][M];
		int g[R][M/10];
		int fac[R],ifac[R];
		
		int C(int n,int m){return (n>=0&&m>=0&&n>=m)?mul(fac[n],mul(ifac[m],ifac[n-m])):0;}
		int F(int i,int x,int y){return mul(dec(fx[i][x+1],fx[i][x]),dec(fy[i][y+1],fy[i][y]));}
	public:
		FoxJumping(){}
		inline int getCount(int Tx,int Ty,int Mx,int My,int R,std::vector<int> bad){
			fac[0]=fac[1]=ifac[0]=1;
			for(int re i=2;i<=R;++i)fac[i]=mul(fac[i-1],i);
			ifac[R]=power(fac[R],mod-2);
			for(int re i=R-1;i;--i)ifac[i]=mul(ifac[i+1],i+1);
			int lim=std::min(Tx,Ty)/10;
			g[0][0]=1;bad.push_back(0);
			for(int &v:bad)v/=10;
			for(int re i=1;i<=R;++i){
				for(int re v:bad){
					for(int re j=lim;j>=v;--j)Inc(g[i][j],g[i-1][j-v]);
				}
			}
			for(int re i=1;i<=Tx+1;++i)fx[0][i]=1;
			for(int re i=1;i<=R;++i){
				for(int re j=1;j<=Tx+1;++j)
				fx[i][j]=add(fx[i][j-1],dec(fx[i-1][j],fx[i-1][j-std::min(j-1,Mx)-1]));
			}
			for(int re i=1;i<=Ty+1;++i)fy[0][i]=1;
			for(int re i=1;i<=R;++i){
				for(int re j=1;j<=Ty+1;++j)
				fy[i][j]=add(fy[i][j-1],dec(fy[i-1][j],fy[i-1][j-std::min(j-1,My)-1]));
			}
			int ans=0;
			for(int re i=0;i<=R;++i){
				int coef=(i&1)?mod-C(R,i):C(R,i);
				for(int re j=0;j<=lim;++j)
				Inc(ans,mul(coef,mul(g[i][j],F(R-i,Tx-j*10,Ty-j*10))));
			}
			return ans;
		}
};

#ifdef zxyoi
#define gc getchar
FoxJumping Solver;

inline int getint(){
	char c;
	while(!isdigit(c=gc()));int num=c^48;
	while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
	return num;
}

int Tx,Ty,Mx,My,R,K;
std::vector<int> vec;

signed main(){
	freopen("FoxJumping.in","r",stdin);
	Tx=getint(),Ty=getint(),Mx=getint(),My=getint(),R=getint(),K=getint();
	while(K--)vec.push_back(getint());
	std::cout<<Solver.getCount(Tx,Ty,Mx,My,R,vec);
	return 0;
}

#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值