星星之火

[jzoj 5664] [GDOI2018Day1模拟4.6] 凫趋雀跃 解题报告(容斥原理)

interlinkage:

https://jzoj.net/senior/#contest/show/2703/3

description:

solution:

考虑容斥原理,枚举不合法的走的步数

$f_{p,x,y}$表示任意走$p$步走到$x$,$y$的方案数

$g_{p,x}$表示走不合法的步走$p$步走到$(10*x,10*x)$的方案数

$g$数组很好得到,发现$f$数组直接暴力转移时间复杂度不对

但是随意走在横轴和竖轴上是独立的,因此我们可以设$fx_{p,x}$表示在横轴上走$p$步走到位置$x$的方案数,同理得到$fy$数组

$f_{p,x,y}=fx_{p,x}*fy_{p,y}$

那么$ans=\sum_{i=0}^{R}\dbinom{R}{i}\sum_{z=0}^{min(Tx,Ty)/10}g_{i,z}*f_{R-i,Tx-10*z,Ty-10*z}$

注意因为零向量不可走,方便处理我们将它加入不可走的数组中即可,因为$0 \mod 10=0$也是成立的

code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;

const int M=1600+15;
const int mo=1e4+7;
int Tx,Ty,Mx,My,R,K;
int fx[M][M],fy[M][M],sum[M],kk[M],g[M][M],C[M][M];
int main()
{
    freopen("jump.in","r",stdin);
    freopen("jump.out","w",stdout);
    scanf("%d%d%d%d%d%d",&Tx,&Ty,&Mx,&My,&R,&K);
    for (int j=0;j<=Tx;j++) sum[j]=1;
    for (int i=1;i<=R;i++)
    {
        for (int j=0;j<=Tx;j++) 
        {
            if (j>Mx) fx[i][j]=((sum[j]-sum[j-Mx-1])%mo+mo)%mo;
            else fx[i][j]=sum[j];
        }
        sum[0]=fx[i][0];
        for (int j=1;j<=Tx;j++) sum[j]=(sum[j-1]+fx[i][j])%mo;
    }
    for (int j=0;j<=Ty;j++) sum[j]=1;
    for (int i=1;i<=R;i++)
    {
        for (int j=0;j<=Ty;j++) 
        {
            if (j>My) fy[i][j]=((sum[j]-sum[j-My-1])%mo+mo)%mo;
            else fy[i][j]=sum[j];
        }
        sum[0]=fy[i][0];
        for (int j=1;j<=Ty;j++) sum[j]=(sum[j-1]+fy[i][j])%mo;
    }
    for (int i=1;i<=K;i++) scanf("%d",&kk[i]);kk[++K]=0;
    g[0][0]=1;
    for (int k=1;k<=R;k++)
    {
        for (int i=0;10*i<=min(Tx,Ty);i++)
        {
            for (int j=1;j<=K;j++)
            if (i>=kk[j]/10)
            {
                (g[k][i]+=g[k-1][i-kk[j]/10])%=mo;
            }
        }
    }
    C[0][0]=1;
    for (int i=1;i<=R;i++)
    {
        C[i][0]=1;
        for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
    }
    int ans=0;
    for (int i=0;i<=R;i++)
    {
        if (i&1)
        {
            for (int z=0;z*10<=min(Tx,Ty);z++) (ans-=1ll*C[R][i]*g[i][z]%mo*fx[R-i][Tx-10*z]%mo*fy[R-i][Ty-10*z]%mo)%=mo;
        }
        else 
        {
            for (int z=0;z*10<=min(Tx,Ty);z++) (ans+=1ll*C[R][i]*g[i][z]%mo*fx[R-i][Tx-10*z]%mo*fy[R-i][Ty-10*z]%mo)%=mo;
        }
    }
    ans=(ans%mo+mo)%mo;
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-04-06 21:34  星星之火OIer  阅读(160)  评论(0编辑  收藏  举报