HDU 4373 Mysterious For(Lucas定理、中国剩余定理)

来源:互联网 发布:structure软件 编辑:程序博客网 时间:2024/04/28 09:47

题目链接:
HDU 4373 Mysterious For
题意:
定义两种循环:
for(int a[i]=0;a[i]<n;++a[i])...
for(int a[i]=a[i1];a[i]<n;++a[i])...
会有num层循环属于第一种循环,给出这些循环的位置,求总共的循环次数是多少?答案mod 364875103
分析:
首先我们考虑第一种循环下面跟k1个第二种循环,总共是k层循环的情况。定义在t层循环到i的次数为ft(i)

  • 对于第一层循环,循环到i,显然有
    f1(i)=1f1(i)=n=C1n
  • 对于第二层循环,因为第二层循环属于第二种循环,想要循环到i,那么相应的第一层循环就要i,也就是
    f2(i)=jif1(j)=i:f2(i)=(n+1)n2=C2n+1
  • 对于第三层循环,同理可得:
    f3(i)=jif2(i)=(i+1)i2f3(i)=12(i2+i)=(n+2)(n+1)(n)6=C3n+2
  • 可以发现对于第k层循环总的循环次数是
    Ckn+k1

接着我们需要计算Ckn+k1 %364875103.比较坑爹的是364875103不是一个素数!但是对364875103进行素数检查时可以发现364875103=973761599而这两个因子都是素数,令mod1=97,mod2=3761599,mod=364875103
m=k,n=n+k1,我们

Cmn  %  modx,Cmn % mod1x1,Cmn % mod2x2

则有:
x x1(%  mod1)x x2(%  mod2)

因为mod1,mod2都是素数,所以x1x2都可以用Lucas那么剩下的问题就是求解上面的同余方程了。
利用中国剩余定理求解同余方程。
我们定义:
inv1:(mod2mod1)mod2inv2:(mod1mod2)mod1

那么答案就是:
x=(inv1x1 +inv2x2)% mod

最后在根据乘法原理易知每段求得的答案需要累乘。

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <climits>#include <cmath>#include <ctime>#include <cassert>#define IOS ios_base::sync_with_stdio(0); cin.tie(0);using namespace std;typedef long long ll;const int mod1 = 97;const int mod2 = 3761599;const ll MOD = 364875103;const int MAX_M = 100010;ll fac1[mod1 + 10], fac2[mod2 + 10], e1, e2;ll quick_pow(ll a, ll b, ll mod){    ll res = 1, tmp = a;    while(b) {        if(b & 1) res = res * tmp % mod;        tmp = tmp * tmp % mod;        b >>= 1;    }    return res;}void init() {    fac1[0] = fac2[0] = 1;    for(int i = 1; i < mod1; ++i) { fac1[i] = fac1[i - 1] * i % mod1; }    for(int i = 1; i < mod2; ++i) { fac2[i] = fac2[i - 1] * i % mod2; }    e1 = quick_pow(mod2, mod1 - 2, mod1) * mod2;    e2 = quick_pow(mod1, mod2 - 2, mod2) * mod1;}ll ex_gcd(ll a, ll b, ll& x, ll& y) {    if(b == 0) {        x = 1, y = 0;        return a;    }    ll r = ex_gcd(b, a % b, y, x);    y -= a / b * x;    return r;}ll inv(ll a, ll n){    ll x, y;    ll d = ex_gcd(a, n, x, y);    if(d == 1) return (x % n + n) % n;    else return 0;}ll C(int n, int m, int id, int mod){    if(m > n) return 0;    ll a, b, c;    if(id == 1) {        a = fac1[n], b = fac1[n - m], c = fac1[m];    } else {        a = fac2[n], b = fac2[n - m], c = fac2[m];    }    //return a * inv(b * c % mod, mod) % mod;  //也可以用扩展欧几里德求逆元    return a * quick_pow(b * c % mod, mod - 2, mod) % mod;}ll Lucas(int n, int m, ll mod, int id){    if(m == 0) return 1;    return C(n % mod, m % mod, id, mod) * Lucas(n / mod, m / mod, mod, id) % mod; }int main(){    init();    int T, n, m, num, data[30], cases = 0;    scanf("%d", &T);    while(T--) {        scanf("%d%d%d", &n, &m, &num);        for(int i = 0; i < num; ++i) { scanf ("%d", &data[i]); }        data[num++] = m;        ll ans = 1;        for(int i = 1; i < num; ++i) {            int k = data[i] - data[i - 1];            ll t1 = Lucas(n + k - 1, k, mod1, 1) * e1 % MOD;            ll t2 = Lucas(n + k - 1, k, mod2, 2) * e2 % MOD;            ans = ans * (t1 + t2) % MOD;        }        printf("Case #%d: %lld\n", ++cases, ans);    }    return 0;}
0 0