Jzoj5644 凫趋雀跃

经典套路容斥题

直接dp做是O(TxTyRk)的可以60分

满分做法又要用到经典的容斥法

我们设f[i][x][y]表示走i步,走到x,y的方案(不考虑不合法向量)

设g[i][x]表示走了i步不合法的向量,走到了(x,x)的方案数

二项式反演得

Answer=∑(-1)^i*C(R,i)*g[i][x]*f[R-i][Tx-x][Ty-x] 

考虑f如何计算,因为x,y两维是独立的

所以有 f[i][x][y]=f[i][x]*f[i][y]  可以O(nm)计算

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 10007
using namespace std;
int n,m,x,y,r,K,t[100],s[1610],S,N,js[1610];
int inv[1610],f[1610][1010],g[2][1610][1010];
inline int ad(int& x,int y){ x=(x+y)%M; }
inline int C(int n,int m){ return n>=m?js[n]*inv[m]%M*inv[n-m]%M:0; }
inline void pow(int x,int k,int& s){
	for(s=1;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
}
int main(){
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	scanf("%d%d%d%d%d%d",&n,&m,&x,&y,&r,&K);
	for(int i=1;i<=K;++i) scanf("%d",t+i),t[i]/=10;
	for(int i=*js=1;i<=r;++i) js[i]=js[i-1]*i%M;
	for(pow(js[r],M-2,inv[r]);;) break; N=min(n,m)/10;
	for(int i=r;i;--i) inv[i-1]=inv[i]*i%M;
	sort(t+1,t+1+K); K=unique(t+1,t+1+K)-t-1;
	for(int i=**f=1,j,k;i<=r;++i)
	for(j=0;j<=N;++j) for(k=0;k<=K;++k) 
	j>=t[k]?ad(f[i][j],f[i-1][j-t[k]]):0;
	for(int i=**g[0]=1,j;i<=r;++i)
	for(j=0;j<=n;++j) s[j]=(g[0][i-1][j]+(j?s[j-1]:0))%M,
	g[0][i][j]=(s[j]-(j>x?s[j-x-1]:0)+M)%M;
	for(int i=**g[1]=1,j;i<=r;++i)
	for(j=0;j<=m;++j) s[j]=(g[1][i-1][j]+(j?s[j-1]:0))%M,
	g[1][i][j]=(s[j]-(j>y?s[j-y-1]:0)+M)%M;
	for(int i=0;i<=r;++i) for(int j=0;j<=N;++j)
	ad(S,f[i][j]*C(r,i)%M*g[0][r-i][n-j*10]%M*g[1][r-i][m-j*10]%M*(i&1?M-1:1));
	printf("%d\n",(S+M)%M);
}

posted @ 2018-04-20 21:07  扩展的灰(Extended_Ash)  阅读(233)  评论(0编辑  收藏  举报