#6374. 「SDWC2018 Day1」网格 二项式反演套二项式反演套二项式反演

题目链接


(60\%)的做法

首先可以把两维分开做,但是在某一维上可以一步走(+0),而不能两维一起(+0),观察发现两维都不变相当于少走一步,也就是说我们可以求出走(le R)步走到终点的方案数,并且是会重复计算的(假设一种方案是走了(R-m)步,那么会在计算(R-n,(nle 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,jle lfloorfrac{min(T_x, T_y)}{G} floorle 100)

第二层的和最内层的反演都是(O(R))到这里已经可以随便跑了

最内层的反演只有两个变量,两维距离的减少量(j),和步数(R')记忆化一下就只要(O(R^2lfloorfrac{min(T_x, T_y)}{G} floor))

大概在(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;
}
原文地址:https://www.cnblogs.com/CMXRYNP/p/9451069.html