[SDWC2018 Day1]网格

14 篇文章 0 订阅
11 篇文章 0 订阅

前言

学了容斥与二项式反演,也该写写题了

题目介绍

题目网址

题意简介

现在有个人,要用 M M M步从 ( 0 , 0 ) (0,0) (0,0)跳到 ( T x , T y ) (T_x,T_y) (Tx,Ty),每次只能向右上方跳(即坐标值只能加),不能在原地跳
给出一些限制:给出 K K K个限制 k i k_i ki,表示两个坐标不能同时加 k i k_i ki。给出一个数 G G G,保证所有 k i k_i ki都是 G G G的倍数,求方案数(模 1 0 9 + 7 10^9+7 109+7

数据范围

T x , T y ≤ 1 0 6 , M x ≤ T x , M y ≤ T y T_x,T_y\le10^6,M_x\le T_x,M_y\le T_y Tx,Ty106,MxTx,MyTy
R ≤ 1000 , 10000 ≤ G ≤ 50000 , K ≤ 50 R\le 1000,10000\le G\le50000,K\le50 R1000,10000G50000,K50

题解

K = 0 K=0 K=0

先考虑 K = 0 K=0 K=0的情况
两个维度,都是 1 0 6 10^6 106级别的,一起做一定不好做
然后发现除了“两维不能同时停在原地”这个限制以外,两维是独立的
考虑对于一维,我们已知其长度 T T T以及单步最大距离 M M M
求不违反规则恰好走 x x x步的方案数
直接 d p dp dp复杂度是 Θ ( T M R ) \Theta(TMR) Θ(TMR)的,显然不行
我们发现至少违反 a a a次规则恰好走 R R R步的方案数很好求
c a l c ( a ) calc(a) calc(a)为至少违反 a a a次规则的方案数,我们发现可以选 a a a步,让这些步一开始就有 M + 1 M+1 M+1的值,那么其无论再加多少都一定是违反规则的
那么就得出 c a l c ( a ) calc(a) calc(a)的式子: c a l c ( a ) = ( R a ) ( T − ( M + 1 ) ∗ a + R − 1 R − 1 ) calc(a)=\binom{R}{a}\binom{T-(M+1)*a+R-1}{R-1} calc(a)=(aR)(R1T(M+1)a+R1)
预处理组合数后计算 c a l c ( a ) calc(a) calc(a)单次 Θ ( 1 ) \Theta(1) Θ(1)
考虑容斥,容易发现我们要求的答案就是 ∑ i = 0 R ( − 1 ) i c a l c ( i ) \sum_{i=0}^R(-1)^icalc(i) i=0R(1)icalc(i)
所以对于一维的答案的复杂度是 Θ ( R ) \Theta(R) Θ(R)的,那么我们把两维都求出来
我们发现,两维的乘积的结果并不是我们想要的恰好 R R R步,而是至多 R R R
所以我们如果要合并,我们不能直接相乘
给定 T , M T,M T,M,设 L ( R ) L(R) L(R)为给至多走 R R R步的方案数, E ( R ) E(R) E(R)为恰好走 R R R步的方案数
L ( R ) = ∑ i = 0 R ( R i ) E ( i ) L(R)=\sum_{i=0}^R\binom{R}{i}E(i) L(R)=i=0R(iR)E(i)
二项式反演
E ( R ) = ∑ i = 0 R ( − 1 ) R − i ( R i ) L ( i ) E(R)=\sum_{i=0}^R(-1)^{R-i}\binom{R}{i}L(i) E(R)=i=0R(1)Ri(iR)L(i)
然后发现求一次 E ( R ) E(R) E(R) Θ ( R 2 ) \Theta(R^2) Θ(R2)的,比较优越

K ≠ 0 K\neq0 K̸=0

然后我们考虑有 K K K限制的情况
我们发现我们要求的是不违反限制的情况,但是并不好求
又发现,至少违反 n n n次数的情况很好统计
同样考虑容斥, g ( x ) g(x) g(x)为至少违反 x x x次的方案数,容易发现我们要求的就是 ∑ i = 0 ∞ ( − 1 ) i g ( i ) \sum_{i=0}^{\infty}(-1)^ig(i) i=0(1)ig(i)
我们先来思考 i i i的范围,由于 k i k_i ki都是 G G G的倍数,而 G ≥ 10000 G\ge10000 G10000,所以 i i i的取值范围是 Θ ( T G ) = Θ ( 100 ) \Theta(\frac TG)=\Theta(100) Θ(GT)=Θ(100)
考虑 d p dp dp来统计 g g g,定义 f i , j f_{i,j} fi,j为至少违反 j j j次、 ∑ k = j ∗ G \sum k=j*G k=jG的方案数,容易发现第二维也是 Θ ( 100 ) \Theta(100) Θ(100)
f i , j f_{i,j} fi,j对答案的贡献系数即 T x − j ∗ G , T y − j ∗ G , x − i T_x-j*G,T_y-j*G,x-i TxjG,TyjG,xi步并且没有任何限制的答案,通过前面 K = 0 K=0 K=0的部分的分析可以得知这部分的复杂度是 Θ ( M x ) \Theta(Mx) Θ(Mx)的,总复杂度大概是 Θ ( 1000 ∗ 1000 ∗ 100 ∗ 100 ) \Theta(1000*1000*100*100) Θ(10001000100100)
貌似能过??而且跑的飞快
当然我们要科学的复杂度,只要预处理 L ( x ) L(x) L(x)即可,我们发现 T T T的取值因为 j j j的不同只有 Θ ( 100 ) \Theta(100) Θ(100)组,步数是 Θ ( R ) \Theta(R) Θ(R)的,一次处理是 Θ ( R ) \Theta(R) Θ(R)
所以总复杂度为 Θ ( T G R 2 ) \Theta(\frac TGR^2) Θ(GTR2)大约 1 0 8 10^8 108左右,再加上根本跑不满,所以很容易就过了

代码

#include<cstdio>
#include<cctype>
#include<algorithm>
namespace fast_IO
{
    const int IN_LEN=10000000,OUT_LEN=10000000;
    char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
    inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
    inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
    inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
//#include<ctime>
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
//template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
    if(x>=10)printe(x/10);
    putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
    if(x<0)putchar('-'),printe(-x);
    else printe(x);
}
const LL mod=1000000007;
inline void md(LL&x){if(x>=mod)x-=mod;}
LL fac[1000001],inv[1000001];
inline LL pow(LL x,LL y)
{
	LL res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
inline LL C(LL x,LL y){return fac[x]*inv[y]%mod*inv[x-y]%mod;}
inline void init()
{
	fac[0]=1;
	for(rg int i=1;i<=1000000;i++)fac[i]=fac[i-1]*i%mod;
	inv[1000000]=pow(fac[1000000],mod-2);
	for(rg int i=1000000;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
int T,Tx,Ty,Mx,My,R,G;
int K,k[51];
LL f[101][101],g[101];
LL calc[101][1001];
inline LL solve(const int x,const int step) 
{
	LL res=0;
	for(rg int i=0;i<=step;i++)
	{
		if((i&1)==(step&1))md(res+=calc[x][i]*C(step,i)%mod);
		else md(res+=mod-calc[x][i]*C(step,i)%mod);
	}
	return res;
}
int main()
{
	init();
	read(Tx),read(Ty),T=min(Tx,Ty),read(Mx),read(My),read(R),read(G);
	for(rg int i=0;G*i<=T;i++)
	{
		const int tx=Tx-G*i,ty=Ty-G*i;
		for(rg int j=R;j>=0;j--)
		{
			if(j*Mx<tx||j*My<ty)break;
			LL valx=0,valy=0;
			for(rg int step=0;step<=j&&(Mx+1)*step<=tx;step++)
				if(step&1)md(valx+=mod-C(j,step)*C(tx-(Mx+1)*step+j-1,j-1)%mod);
				else md(valx+=C(j,step)*C(tx-(Mx+1)*step+j-1,j-1)%mod);
			for(rg int step=0;step<=j&&(My+1)*step<=ty;step++)
				if(step&1)md(valy+=mod-C(j,step)*C(ty-(My+1)*step+j-1,j-1)%mod);
				else md(valy+=C(j,step)*C(ty-(My+1)*step+j-1,j-1)%mod);
			calc[i][j]=valx*valy%mod;
		}
	}
	read(K);
	for(rg int i=1;i<=K;i++)read(k[i]);
	std::sort(k+1,k+K+1);
	K=std::unique(k+1,k+K+1)-k-1;
	for(rg int i=1;i<=K;i++)k[i]/=G;
	f[0][0]=1,g[0]=solve(0,R);
    for(rg int i=1;G*i<=T;i++)
    	for(rg int j=0;G*j<=T;j++)
    	{
    		for(rg int kind=1;kind<=K;kind++)
    			if(k[kind]<=j)
    				md(f[i][j]+=f[i-1][j-k[kind]]);
    		md(g[i]+=solve(j,R-i)*f[i][j]%mod);
    	}
    LL ans=0;
    for(rg int i=0;G*i<=T;i++)
		if(i&1)md(ans+=mod-g[i]*C(R,i)%mod);
		else md(ans+=g[i]*C(R,i)%mod);
	print(ans);
	return flush(),0;
}

总结

这道题的非常巧妙,多次运用容斥与反演的技巧,值得深思
致谢:感谢yx2003在本题上对我的指导

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值