#6374. 「SDWC2018 Day1」网格 二项式反演套二项式反演套二项式反演
题目链接
\(60\%\)的做法
首先可以把两维分开做,但是在某一维上可以一步走\(+0\),而不能两维一起\(+0\),观察发现两维都不变相当于少走一步,也就是说我们可以求出走\(\le R\)步走到终点的方案数,并且是会重复计算的(假设一种方案是走了\(R-m\)步,那么会在计算\(R-n,(n\le m)\)步的时候被算\(\dbinom{m}{n}\)次),最后套个二项式反演就行了。
然后考虑怎么求\(x\)一维的答案,一眼生成函数,然后\(TLE\)了
由于步数\(R\)很小,考虑容斥
我们可以枚举一个\(i\)并求出有\(\ge i\)步距离\(>M_x\)的方案数(先分配\(i*(M_x+1)\)的距离,剩下插板法,还要乘\(\dbinom{R}{i}\)),同样,这里每种方案也会被每个子集都计算一次,套个二项式反演就行了。
这样就有\(60pts\)了
\(100\%\)的做法
在\(60\%\)的基础上,考虑如何删掉包含了\((k_i,k_i)\)的方案
令\(f_{i,j}\)表示走\(i\)步,每步都是\((k_i,k_i)\)的不合法向量,共走了\((j*G,j*G)\)的方案数
这个可以很轻松地转移..
然后对于每一个\(f_{i,j}\),用\(60\%\)的做法求出含有至少这\(i\)步,\((j*G,j*G)\)的不合法距离的方案数(还要乘\(\dbinom{R}{i}\))
这里也套个二项式反演就行了。
感觉会\(T\)飞啊,那就分析一下复杂度。
\(i,j\le \lfloor\frac{min(T_x, T_y)}{G}\rfloor\le 100\)
第二层的和最内层的反演都是\(O(R)\),到这里已经可以随便跑了
最内层的反演只有两个变量,两维距离的减少量\(j\),和步数\(R'\),记忆化一下就只要\(O(R^2\lfloor\frac{min(T_x, T_y)}{G}\rfloor)\)了
大概在\(10^8\)级别(没错的话),但是跑得飞快,记忆化没快多少
#include<cstdio>
#include<algorithm>
#include<ctype.h>
#include<string.h>
#include<math.h>
using namespace std;
#define ll long long
#define travel(i,x) for(int i=h[x];i;i=pre[i])
const int N = 1001005, P = 1000000007, K = 55, M = 105, MAXR = 1005;
int Tx, Ty, Mx, My, R, G, k, ans, lim, last, d[K], g[MAXR], vis[MAXR], f[M][M], fac[N], inv[N];
inline int Pow(ll x, int y=P-2){
ll ass=1;
for(; y; y>>=1, x=x*x%P) if(y&1) ass=ass*x%P;
return ass;
}
inline int C(int n, int m){ return (ll)fac[n]*inv[m]%P*inv[n-m]%P;}
inline int calc(int Tx, int Ty, int R){
if(vis[R]==Tx) return g[R];
int tota=0, totb=0;
for(int i=0; i<=R && i*(Mx+1)<=Tx; ++i) tota=(tota+(i&1?-1ll:1ll)*C(Tx-i*(Mx+1)+R-1, R-1)*C(R, i))%P;
for(int i=0; i<=R && i*(My+1)<=Ty; ++i) totb=(totb+(i&1?-1ll:1ll)*C(Ty-i*(My+1)+R-1, R-1)*C(R, i))%P;
return vis[R]=Tx, g[R]=((ll)tota*totb%P+P)%P;
}
inline int solve(int Tx, int Ty, int R){
int ass=0;
for(int i=0; i<=R; ++i) ass=(ass+(i&1?-1ll:1ll)*calc(Tx, Ty, R-i)*C(R, i))%P;
return ass;
}
int main() {
scanf("%d%d%d%d%d%d%d", &Tx, &Ty, &Mx, &My, &R, &G, &k);
fac[0]=1;
lim=max(Tx, Ty)+R-1;
for(int i=1; i<=lim; ++i) fac[i]=(ll)fac[i-1]*i%P;
inv[lim]=Pow(fac[lim]);
for(int i=lim; i; --i) inv[i-1]=(ll)inv[i]*i%P;
for(int i=1; i<=k; ++i) scanf("%d", d+i), d[i]/=G;
sort(d+1, d+k+1);
int now=0;
for(int i=1; i<=k; ++i) if(d[i]!=d[i-1]) d[++now]=d[i];
k=now;
lim=min(Tx, Ty)/G;
f[0][0]=1;
for(int i=1; i<=lim; ++i) for(int j=1; j<=lim; ++j) for(int t=1; t<=k && d[t]<=j; ++t) f[i][j]=(f[i][j]+f[i-1][j-d[t]])%P;
last=-1;
for(int j=0; j<=lim; ++j) for(int i=0; i<=lim; ++i) if(f[i][j]) ans=(ans+(i&1?-1ll:1ll)*solve(Tx-j*G, Ty-j*G, R-i)*C(R, i)%P*f[i][j])%P;
return printf("%d", (ans+P)%P), 0;
}