hdu 4373 组合数学

http://acm.hdu.edu.cn/showproblem.php?pid=4373

哎,多校的题。感觉是个很简单的数学问题,但是比赛的时候肿么都想不出来。今天决心把它给做了。

哎,原来以为是一道简单数学题呢,还用到Lucas,还用到中国剩余定理啊,哎。。。

于是我就去研究研究了一下中国剩余定理。终于懂了。参见黑书p230页。

怎么描述中国剩余定理的解法呢,我想想。

对于两个数m,n(m和n互质),满足x = a1 (mod m)和 x= a2 (mod n)的x为:

n * (n关于模m的逆元 * a1) + m *(m关于模n的逆元 * a2)= x;


转的,他写得好:http://blog.csdn.net/cyberzhg/article/details/7877465
题意:
m个for循环嵌套,有两种形式,第一类从1开始到n,第二类从上一层循环当前数开始到n,第一层一定是第一种类型,问总的循环的次数对364875103取余的结果。

首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把m个嵌套分为几个不同的部分,每一个部分由第一类循环开始,最终结果相乘就可以。
剩下的就是第二类循环的问题,假设一个m层循环,最大到n,
只有第一层:循环n次。C(n, 1)
只有前两层:循环n + (n - 1) + ... + 1 = (n + 1) * n / 2 = C(n + 1, 2)
只有前三层:
(n + 1) * n / 2 + n * (n - 1) / 2 + ... + 1
= (1 + 2 + ... + n) / 2 + (1 + 4 + ... + n^2) / 2
= (n + 1) * n / 2 / 2 + n * (n + 1) * (2n + 1) / 6 / 2
= (n + 2) * (n + 1) * n / 6 = C(n + 2, 3)
……
找规律得第m层:C(n + m - 1, m)

接下来的问题就是如何求出这些东西对364875103的模,由于n和m都非常大,暴力统计必然是不行的。
364875103 = 97 * 3761599 (比赛时候哪有心情拆数玩,这个太不厚道了)
Lucas(n, m, p) = c(n % p,m % p) * Lucas(n / p, m / p, p);
用Lucas定理分别求出两个质数的余数,然后利用中国剩余定理求出对364875103的余数。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
#define MOD1 3761599
#define MOD2 97
#define MOD 364875103
using namespace std;
int t,lop_len,lop_num,fir_num,a[100009];
__int64 ans,p1[3761610],p2[110],c1,c2;
__int64 pow_mod(__int64 x, __int64 y, __int64 mod){
    __int64 res = 1;
    while(y){
        if(y & 1)
            res =( res * x ) % mod;
        x = (x * x) % mod;
        y >>= 1;
    }
    return res;
}
__int64 cm(__int64 m, __int64 n, __int64 mod, __int64 p[]){//C(m,n),m取n
    if(m < n) return 0;
    __int64 res = (p[m] * pow_mod(p[n], mod - 2, mod) ) % mod;
    return ( res * pow_mod(p[m - n] , mod - 2, mod)) % mod;
}

void init(){
    p1[0] = 1;  p2[0] = 1;
    for(int i = 1; i <= MOD1; i ++){
        p1[i] = p1[i - 1] * i % MOD1;
    }
    for(int i = 1; i <= MOD2; i ++){
        p2[i] = p2[i - 1] * i % MOD2;
    }
    c1 = MOD2 * pow_mod(MOD2, MOD1 - 2, MOD1);
    c2 = MOD1 * pow_mod(MOD1, MOD2 - 2, MOD2);
}
__int64 lucas(__int64 m, __int64 n, __int64 mod,__int64 p[]){
    __int64 res = 1;
    while(n && m && res){
        res = (res * cm(m % mod,n % mod,mod,p));
        n /= mod;
        m /= mod;
    }
    return res;
}
int main(){
//3761599  97 拆数
//    for(int i = 2; i <= 1000; i ++)
//        if(364875103 % i == 0)
//            printf("%d  %d",364875103 / i,  i);
    init();
    //求c1,c2和记录
    //c1 为mod2 * (mod2 对 mod1 的逆元),即c1 % mod1 == 1, c2 % mod2 == 0;
    //c2 为mod1 * (mod1 对 mod2 的逆元),即c2 % mod2 == 1, c1 % mod1 == 0;
    scanf("%d",&t);
    for(int ca = 1; ca <= t; ca ++){
        scanf("%d%d%d",&lop_len,&lop_num,&fir_num);
        for(int i = 0; i < fir_num; i ++)
            scanf("%d",a + i);//index of type 1, started from index 0
        a[fir_num] = lop_num;//这个,需要理解和注意
        ans = 1;
        for(int i = 0; i < fir_num; i ++){
            int tmp = (a[i + 1] - a[i]);
            __int64 m1 = lucas(tmp + lop_len - 1, tmp, MOD1, p1);
            __int64 m2 = lucas(tmp + lop_len - 1, tmp, MOD2, p2);
            __int64 mm = (m1 * c1 + m2 * c2) % MOD;
            ans = (ans * mm) % MOD;
        }
        printf("Case #%d: %I64d\n",ca,ans);
    }
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值