【HDU5794】 A Simple Chess(Lucas+容斥)

记录一个菜逼的成长。。

参考自:
http://blog.csdn.net/lqybzx/article/details/52124404
http://www.cnblogs.com/zhengguiping–9876/p/5785594.html

貌似之前见过一些这种棋盘算方案数的的题。。
或许算经典的题型吧。

题目大意:
有一个n*m的棋盘,有一个棋子只能走“日”字形。
棋盘上有一些障碍物。
问从(1,1)走到(n,m)的方案数。
这里写图片描述
图片来源:http://www.cnblogs.com/zhengguiping–9876/p/5785594.html

容斥应该算是一种逆向思维吧。
直接求不好求。
我们可以用总方案数减去不可行的方案数。
我们用一个数组来ans[i]来表示到不经过障碍物到达第i个障碍物的方案数。
需要先对障碍物进行从左到右排序。
对于每一个障碍物,
先求出到达这个障碍物i的方案数,
再枚举这个障碍物之前的障碍物j,减去从(1,1)到j障碍物*从j障碍物到i障碍物的方案数,两个过程均不经过中间障碍物。
我们将终点放入障碍物数组,那么答案就是ans[r+1];

由图上的路径可以看出是杨辉三角的构造。
我们需要将坐标转换一下,使得方案数可以用组合数求出。
求组合数可以用Lucas定理的模板。
注意:
如果不用预处理阶乘数组的模板,会吃T.

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <list>
#include <deque>
#include <cctype>
#include <bitset>
#include <cmath>
#include <cassert>
using namespace std;
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define bp __builtin_popcount
#define pb push_back
#define mp make_pair
#define fin freopen("D://in.txt","r",stdin)
#define fout freopen("D://out.txt","w",stdout)
#define lson t<<1,l,mid
#define rson t<<1|1,mid+1,r
#define seglen (node[t].r-node[t].l+1)
#define pi 3.1415926
#define exp  2.718281828459
#define lowbit(x) (x)&(-x)
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
typedef vector<PII> VPII;
const int INF = 0x3f3f3f3f;
const LL MOD = 110119;
struct node{
    LL x,y;
}a[110];
bool cmp(node a,node b)
{
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}
LL fact[110119] = {1};
void init(int n)
{
    for( int i = 1; i <= n; i++ ){
        fact[i] = fact[i-1] * i % MOD;
    }
}
LL PowerMod(LL a, LL b, LL c)
{
    LL ans = 1;
    a = a % c;
    while(b>0)
    {
        if(b % 2 == 1)
        ans = (ans * a) % c;
        b = b/2;
        a = (a * a) % c;
    }
    return ans;
}
LL C(LL n, LL m)
{
    if(m>n)return 0;
    if(m == 0)return 1;
    LL ans = fact[n] * PowerMod(fact[m], MOD-2,MOD) % MOD * PowerMod(fact[n-m], MOD-2,MOD) % MOD;
    return ans;
}
LL Lucas(LL n, LL m)
{
    if(n<0 || m<0)return 0;///会出现不可达的情况,所以注意判断,否则会re;
    if(m > n) return 0;
    if(m == 0) return 1;
    return C(n%MOD, m%MOD) * Lucas(n/MOD, m/MOD) % MOD;
}
LL ans[110];
LL solve(int i,int j)
{
    LL n = a[j].x - a[i].x,m = a[j].y - a[i].y;
    if((n+m)%3LL != 0)return 0;
    LL step = (n + m) / 3LL;
    n -= step;
    m -= step;
    if(n < 0 || m < 0)return 0;
    return Lucas(n+m,m);

}
int main()
{
    init(110119LL);
    LL n,m;
    int r,cas = 1;
    while(~scanf("%lld%lld%d",&n,&m,&r)){
        for( int i = 1; i <= r; i++ ){
            scanf("%lld%lld",&a[i].x,&a[i].y);
        }
        sort(a+1,a+1+r,cmp);
        a[0].x = 1,a[0].y = 1;
        a[r+1].x = n,a[r+1].y = m;
        for( int i = 1; i <= r + 1; i++ ){
            ans[i] = solve(0,i);
            for( int j = 1; j < i; j++ ){
                ans[i] = (ans[i] - ans[j] * solve(j,i) % MOD + MOD) % MOD;
            }
        }
        printf("Case #%d: %lld\n",cas++,ans[r+1]);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值