(HDU 5794)2016 Multi-University Training Contest 6 A Simple Chess (Lucas、容斥)

思路

出题人说这是一道Lucas+容斥的水题。。
哭晕在厕所。。
写一下是按马(没有马脚)的行走规则来移动的,且每次只能移动到右下方。
左上角坐标为 (1,1) ,右下角坐标为 (n,m)
注意左上角没有障碍物但是右下角可能有.

步数坐标方案数
1(1,1)1
2(2,3)1
2(3,2)1
3(3,5)1
3(4,4)2
3(5,3)1
4(4,7)1
4(5,6)3
4(6,5)3
4(7,4)1

所以方案数就是一个侧着的杨辉三角
步数: (x+y)/3
能够到达的点的坐标满足:

(x+y)mod3=2

所以答案就是 (1,1) (n,m) 的方案数,减去以障碍物 (x,y) 为中心的两端方案数的乘积,因为有可能重复减,所以要容斥一下(这也叫容斥真是醉了),对于每个障碍物,遍历减去之前计算过的障碍物的方案数与障碍物到这个点的方案数的乘积

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=1e2+50;

const double PI=acos(-1);

using namespace std;

/*
逆元定义:x为a的乘法逆元,(b/a) (mod Mod) = (b * x) (mod Mod)
 x表示a的逆元。并且 a*x ≡ 1 (mod Mod )  
 注意:只有当a与Mod互质的时候才存在逆元
以下两种写法都必须要求MOD为素数
*/
typedef long long ll;
const int Mod=110119;
ll invs[Mod+50];
ll fac[Mod+50];
//1 预处理
void initInv(int Mod){
    invs[1]=1;
    for (int i=2;i<Mod;i++)
        invs[i]=invs[Mod%i]*(Mod-Mod/i)%Mod;
}
//2 直接求
ll inv(int a,int mod){
    return(a==1?1:inv(mod%a,mod)*(mod-mod/a)%mod);
} 
//3 inv(x)=x^(mod-2)
ll qPow(ll x,ll n,ll mod){
    ll ret=1;
    while(n){
        if (n&1) ret=(ret*x)%mod;
        x=(x*x)%mod;
        n>>=1;
    }
    return ret; //x^n%mod
}

void initFac(int Mod){
    //n!
    fac[0]=1;
    for(int i=1;i<=Mod;i++){
        fac[i]=fac[i-1]*i%Mod;
    }
}

ll lucas(ll n,ll k,ll mod){
    //C_n^k%mod
    if (n<0 || k<0 || k>n) return 0;
    ll ret=1;
    while(n && k){
        ll np=n%mod,kp=k%mod;
        if (np<kp) return 0;
        ret=(ret*fac[np]%mod)*invs[fac[kp]*fac[np-kp]%mod]%mod;
      //  ret=(ret*fac[np]%mod)*inv(fac[kp]*fac[np-kp]%mod,mod)%mod;
      //  ret=(ret*fac[np]%mod)*qPow(fac[kp]*fac[np-kp]%mod,mod-2,mod)%mod;
        n/=mod; k/=mod;
    }
    return ret;
}

typedef pair<ll,ll> Pll;
ll dp[maxn];

ll get(ll x,ll y){
    if (x<=0 || y<=0) return 0;
    ll step=x+y-2;
    ll lay=step/3;
    ll ways=min(x,y)-lay-1;
    if (step%3) return 0;
    return lucas(lay,ways,Mod);
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
     //   freopen("1002.in","r",stdin);
    #endif
    initFac(Mod);
    initInv(Mod);

    int r; 
    ll n,m;
    int cs=0;
    while(~scanf("%lld %lld %d",&n,&m,&r)){
        vector<Pll> v;
        mem(dp,0);
        ll ans,x,y;
        for(int i=0;i<r;i++){
            scanf("%lld %lld",&x,&y);
            v.push_back(Pll(x,y));
        }
        v.push_back(Pll(n,m));
        sort(v.begin(),v.end());
        for(int i=0;i<=r;i++){
            ll xi=v[i].first,yi=v[i].second;
            dp[i]=get(xi,yi);
            for(int j=0;j<i;j++){
                ll xj=v[j].first,yj=v[j].second;
                dp[i]=(dp[i]-dp[j]*get(xi-xj+1,yi-yj+1)%Mod+Mod)%Mod;
            }
        }
        ans=dp[r];
        printf("Case #%d: %lld\n",++cs,ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值