HDU 5794 A Simple Chess(多校,dp,容斥)

题意:一匹”马”在棋盘上( 1 ,1)的位置,每次跳跃时横纵坐标都必须增大.棋盘上还有 K 个障碍物(保证不在(1, 1 )处).求跳到(n, m )的方案数,对素数P= 110119 取模.

解题思路: 存障碍点的时候要进行筛选,从( 0 ,0)点到( n ,m)不经过的障碍点不存入,之后对点进行按照 x y进行,进行了这个预处理之后后面的dp就很简单了。主要是结合lucas定理,当lucas中传入的值 0时注意输出0,这点不要忘记处理,不然会RE。代码中注释的非常清楚,不懂得可以看一下每一步的实现,想着挺麻烦的,其实实现起来还挺简单的细心一点把细节处理好就行。

AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <complex>
#include <cstdio>
using namespace std;
#define ll long long
#define mod 110119
ll n,m,r,ca,cnt,a,b,rig,up;
ll factorial[110200];

ll mod_pow(ll a,ll n,ll p)
{
    ll ret=1,A=a;
    for(; n ; A=(A*A)%p,n>>=1) if(n & 1)ret=(ret*A)%p;
    return ret;
}

void init_factorial(ll p)
{
    factorial[0] = 1;
    for(ll i = 1;i <= p;i++)factorial[i] = factorial[i-1]*i%p;
}

ll C(ll a,ll k,ll p) //求C(n,m)%p p最大为10^5。a,b可以很大!  a个数中挑k个的组合数
{
    ll re = 1;
    for(; a && k ; a /= p , k /= p){
        ll aa = a%p;ll bb = k%p;
        if(aa < bb) return 0; //这个是最后的改动!
        re = re*factorial[aa]*mod_pow(factorial[bb]*factorial[aa-bb]%p,p-2,p)%p;//这儿的求逆不可先处理
    }
    return re;
}
ll Lucas(ll a,ll k ,ll p){
    if(a<0 || k<0 || a<k)return 0;
    else return C(a,k,p);
}
///以上为lucas算法,将输入为if(a<0 || k<0 || a<k)return 0;
///时直接输出0,当是就是没加这个判断条件,然后就一直RE
struct Point
{
    ll x,y;
    bool operator < (const Point & a) const
    {
        if(x==a.x)  return y<a.y;
        return x<a.x;
    }
}rock[105];///重载小于号,直接sort预处理减少循环


ll dp[105];///dp[]中存的是第i个位,不经过i之前的阻碍点能到达等到达i点的路径个数。
int main()
{
//    freopen("1002.in","r",stdin);
//    freopen("data.out","w",stdout);
    init_factorial(mod);
    ca=1;
    ll x,y;
    while(~scanf("%I64d%I64d%I64d",&n,&m,&r))
    {
        n--;///题目中坐标从1开始,代码中从0开始,方便取模
        m--;
        cnt=0;///cnt存储符合条件的点,从1开始
        for(ll i=0;i<r;i++)
        {
            scanf("%I64d%I64d",&x,&y);
            x--;
            y--;
            ll tx=x;
            ll ty=y;
            a=2*y-x;
            b=2*x-y;
            if(a%3==0&&b%3==0&&a/3>=0&&b/3>=0&&x>=0&&x<=n&&y>=0&&y<=m)///这层的判断,去除不能从(0,0)到达当前点的点
            {
                y=m-y;
                x=n-x;
                a=2*y-x;
                b=2*x-y;
                if(a%3==0&&b%3==0&&a/3>=0&&b/3>=0&&x>=0&&x<=n&&y>=0&&y<=m)///这层判断去除从当前点不能到达(n,m)的点
                {
                    rock[++cnt].x=tx;
                    rock[cnt].y=ty;
                }
            }
        }
        sort(rock+1,rock+cnt+1);///经过筛选之后,rock中所有的点都能到达,并且到达(n,m)点
        if((2*m-n)%3!=0||(2*n-m)%3!=0||(2*m-n)/3<0||(2*n-m)/3<0)///当从(0,0)点不能到达(n,m)点直接输出0
        {
            printf("Case #%I64d: %I64d\n",ca++,(ll)0);
            continue;
        }
        rock[++cnt].x=n;///将(n,m)点存入rock中,这样dp[cnt]直接是答案了
        rock[cnt].y=m;
        for(ll i=1;i<=cnt;i++)
        {
            a=(2*rock[i].y-rock[i].x)/3;
            b=(2*rock[i].x-rock[i].y)/3;
            dp[i]=Lucas(a+b,a,mod);先将地i个点的路径记录下来,
            for(ll j=1;j<i;j++)
            {
                ll ta=rock[i].x-rock[j].x;
                ll tb=rock[i].y-rock[j].y;
                ll taa=(2*ta-tb)/3;
                ll tbb=(2*tb-ta)/3;
                if(rock[i].x>=rock[j].x&&rock[i].y>=rock[j].y)
                    dp[i]=((dp[i]-dp[j]*Lucas(taa+tbb,taa,mod))%mod+mod)%mod;///减去经过之前的点的路径个数

            }

        }
        printf("Case #%I64d: %I64d\n",ca++,dp[cnt]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值