【HDU 4373】【Lucas定理+中国剩余定理】 Mysterious For

传送门:HDU4373 Mysterious For

题意:
m个for循环嵌套,有两种形式,第一类从1开始到n,第二类从上一层循环当前数开始到n,第一层一定
是第一种类型,求总的循环的次数对364875103取余的结果。
思路:
首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把m个嵌套分为几个不
同的部分,每一个部分由第一类循环开始,最终结果相乘即可。
剩下的就是第二类循环的问题,
假设一个m层循环,最大到n,分析一下得到如下结果:

定义在 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 <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << "  " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
using  namespace  std;

#define mst(ss,b) memset(ss,b,sizeof(ss));
#define rep(i,k,n) for(int i=k;i<=n;i++)

template<class T> void read(T&num) {
    char CH; bool F=false;
    for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
    for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
} 

const int N=25;
const int mod1=97;
const int mod2=3761599;
const int mod=mod1*mod2;

int n,m,k;
int a[N];
ll fac1[mod1+10],fac2[mod2+10];
ll inv1,inv2;

ll pow_mod(ll x, ll n, ll p){  
  ll res=1;  
  while(n>0){  
    if(n&1)res=res*x%p;  
    x=x*x%p;  
    n>>=1;  
  }  
  return res;  
} 

ll C(ll n,ll m,ll p,ll fac[]){
  if(m>n) return 0;
  return fac[n]*pow_mod(fac[m]*fac[n-m]%p, p-2, p)%p;
}

ll lucas(ll n,ll m,ll p,ll fac[]){
  if(m==0) return 1;
  return ( C(n%p, m%p, p, fac)*lucas(n/p, m/p, p, fac) )%p;
}

void init(){
  fac1[0]=1;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;
  inv1=mod2*pow_mod(mod2, mod1-2, mod1);
  inv2=mod1*pow_mod(mod1, mod2-2, mod2);
}

int  main(){
  init();
  int t,kase=1;read(t);
  while(t--){
    read(n),read(m),read(k);
    rep(i, 0, k-1)read(a[i]);
    a[k]=m;
    ll ans=1;
    rep(i, 0, k-1){
      ll m1=lucas(a[i+1]-a[i]+n-1, a[i+1]-a[i], mod1, fac1);
      ll m2=lucas(a[i+1]-a[i]+n-1, a[i+1]-a[i], mod2, fac2);
      ll mm=(m1*inv1+m2*inv2) % mod;
      ans=ans*mm % mod;
    }
    printf("Case #%d: %I64d\n", kase++, ans);
  }
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值