XDOJ1146 - 组合数学五之Fate zero

描述:

Fate是讲述这一场围绕着圣杯而展开的战斗。在战斗中的七个参与者可以召唤出一个历史上的英雄,来为自己战斗。不过这样有很多弊端,比如说会引发一些历史遗留问题(兰斯洛特见到亚瑟王什么的,项羽见到刘邦什么的…………)。而且有时还会引发不必要的误会,比如下面一例:

 

最近,吉尔.德.莱斯一直缠着亚瑟王,而且还说他是圣女贞德再世,以此引发了一系列的骚扰。终于有一天,亚瑟王忍无可忍了,决定去讨伐莱斯。作为一个小有名气的法师,莱斯的保护措施是很完善的。

他有m面不同颜色的每条边均不等的n边凸边形的魔法盾。亚瑟王的每一次攻击都是将一个凸多边形延其某个对角线斩开,使其变为两个凸多边形。当在一种颜色的凸多边形的魔法盾中只有三角形时,就会变得能量不稳定,这时只要消灭其中一个三角形,这种颜色的能量盾就会消失。问:亚瑟王如果要完全破处莱斯的防御有多少种不同的方式?(斩击无先后顺序的区别)

 

Input

每一行有一个m,n(3<n<10^18,1<=m<10^18)

Output

每一行输出一个可能的放法数(模10007取余)

Sample Input

1 3
2 4

Sample Output

1
4

解题思路:

== 分析 ==

 考虑1块n边形魔法盾的情况, 设有cata[n]种分割成三角形的办法,则答案为cata[n],当有m个魔法盾时,每个都有cata[n]种办法,所以答案为cata[n]的m次幂

这里的cata[n]就属于卡塔兰数列,具体地说n边形的三角形剖分方案数是卡塔兰数列的第n-2项.

== 卡塔兰数 ==

 公式为c(2*n,n)/(n+1), 考虑到求模的问题这个形式很不方便, 可以展开成c(n,2*n)-c(n+1,2*n) (通过顶端链接可以查到),将组合数按定义展开,略作计算就能达到.

 卡塔兰数的通项公式可以利用它的递归性质,通过生成函数得到,具体可以参考<<The art and craft of problem solving>> 

 (中文名:怎样解题,不是波利亚那部,图书馆就有,不过要等我先还......囧) 

 卡塔兰数有很多奇妙的性质,诞生出很多经典问题,有必要深入了解,传说有本名为<<具体数学>>的奇书记载着卡塔兰数的组合证明,有缘的话30就能买到=。=...

== 卢卡斯定理 ==

 本题需要对组合数求模,当n取某些值时,不与模数p互素,无法求其逆元,需要利用卢卡斯定理.

#include<iostream>
using namespace std;

const int P = 10007;
long long fastPow(long long x,long long y,int p)
{
    long long ans = 1;
    while(y)
    {
        if(y&1)
            ans = ans*x%p;
        y >>= 1;
        x = x*x%p;
    }
    return ans;
}
long long factorial[P+1];

void init(long long p)
{
    factorial[0] = 1;
    for(int i = 1;i <= p;i++)
        factorial[i] = factorial[i-1]*i%p;

}

long long Lucas(long long a,long long k,long long p) //求C(n,m)%p p最大为10^5。a,b可以很大!
{
    long long re = 1;
    while(a && k)
    {
        long long aa = a%p;long long bb = k%p;
        if(aa < bb) return 0;
        re = re*factorial[aa]*fastPow(factorial[bb]*factorial[aa-bb]%p,p-2,p)%p;//这儿的求逆不可先处理
        a /= p;
        k /= p;
    }
    return re;
}
long long getCatan(long long n)
{
    return (Lucas(2*n,n,P)-Lucas(2*n,n+1,P)+P)%P;
}
int main()
{
    long long n,m;
    init(P);
    while(cin>>m>>n)
    {
        m %=(P-1);
        n = getCatan(n-2);
        cout<<fastPow(n,m,P)<<endl;
    }
    return 0;
}

 

最后欢迎大家访问我的个人网站: 1024s

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值