zoukankan      html  css  js  c++  java
  • #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;
    }
    
  • 相关阅读:
    6.Redis 哈希(Hash)的命令
    5.redis中String类型数据操作的命令
    4.redis中的key命令
    3.redis客户端连接服务器
    Bomblab
    leetcode multiply-strings
    datalab
    leetcode max-points-on-a-line
    os
    python 实现简单的端口扫描器
  • 原文地址:https://www.cnblogs.com/CMXRYNP/p/9451069.html
Copyright © 2011-2022 走看看