【spoj SEQN】【hdu 3439】Sequence

题意:

给出n、m、k 求C(n,k)*H(n-k)%m的值 H(n-k)为错排公式

题解:

先算H(n-k)

计算H(n)有个通式:

H(n)=(-1)^n+((-1)^(n-1))n+((-1)^(n-2))n(n-1)+...+n(n-1)(n-2)...3

证明详见维基百科:

http://zh.wikipedia.org/wiki/%E9%94%99%E6%8E%92%E9%97%AE%E9%A2%98#.E7.AE.80.E5.8C.96.E5.85.AC.E5.BC.8F

因为我们是要算H(n-k)mod m的值 显然他的前不超过m项是>0的 而其它都为0 枚举求解即可

_________________________________________________________________________________

再说C(n,k)%m

我们知道世界上有个叫 Lucas 定理的东西:

C(n, m) % p = C(n / p, m / p) * C(n % p, m % p) % p

但是它要求p是质数 所以它和本题的正解没什么关系- -

_________________________________________________________________________________

由于除的数可能不与m互质 所以不能用乘法逆元

不妨将m分解 m=p1^a1*p2^a2*...*pn^an

分别计算C(n,k)%(pi^ai) 的值 最后用中国剩余定理计算答案

于是问题转换为求C(n,k)%(p^a)的值

C(n,k)=n!/((n-k)!*k!)

为了让乘法逆元能能使用 只要将n!、(n-k)!、k! 所有p的倍数 提出来单独计算即可

如果n比较小 枚举计算不为p的倍数的数的积即可 但是这里n<=10^9 我们就需要用一些比较高端的方法

设A(n)=(1*2*..*(p-1)*(p+1)*...*(2p-1)*(2p+1)*...*n)

n!=A(n)*(p*2p*...*(n/p)p)

   =A(n)*(n/p)!*p^(n/p)

   =A(n)*A(n/p)*(n/p/p)!*p^(n/p/p)*p^(n/p)

   =...

就是对n!不断的分解为A(n)*(n/p)!*p^(n/p) 再递归处理(n/p)! 直到n/p为0

当然在计算的同时 要累加/减 p的指数sum

这样就能求出C(n,k)不包含p的乘积了 最后再乘p^sum 即为C(n,k)%(p^a)

_________________________________________________________________________________

问题结束了吗- - 显然木有 对于A(n)还需要用O(n)的时间求出 TLE!

设r=p^a

这里A(n)的值是要模r的 我们可以这样转换

A(n)=1*2*..*(p-1)*(p+1)*...*(2p-1)*(2p+1)*...*n

      =(1*2*...*(r-1))*((r+1)*(r+2)*...*(2r-1))*(...*n)

      =(1*2*...*(r-1))*(1*2*...*(r-1))*...*(1*2*...*n%r) (mod r)

我们就可以预处理save[i]存下1*2*...*i (p的倍数不乘,i<=r-1)

A(n)=save[r-1]^(n/r)*save[n%r] 快速幂就可用log(n)的时间求出A

加上预处理O(r) r<=m<=10^5

最后乘m的质因数的个数blabla- -具体多少我也不知道了 反正能过

_________________________________________________________________________________

代码:

 1 #include <cstdio>
 2 typedef long long ll;
 3 const ll M=100001;
 4 ll n,k,m,t,a[M],r[M],ans,p[M],pri[M],bo[M],zs[M],save[M],tot;
 5 void makepri(){
 6     for (ll i=2;i<M;i++){
 7         if (!bo[i]) pri[++pri[0]]=i;
 8         for (ll j=1;j<=pri[0] && i*pri[j]<M;j++){
 9             bo[i*pri[j]]=1;
10             if (!(i%pri[j])) break;
11         }
12     }
13 }
14 ll mi(ll a,ll b,ll mo){
15     ll res=1%mo;
16     for (;b;b>>=1){
17         if (b&1) res=res*a%mo;
18         a=a*a%mo;
19     }
20     return res;
21 }
22 ll extgcd(ll &x,ll &y,ll a,ll b){
23     if (!b){
24         x=1,y=0;
25         return a;
26     }else{
27         ll res=extgcd(x,y,b,a%b);
28         ll t=x;
29         x=y;
30         y=t-a/b*y;
31         return res;
32     }
33 }
34 ll getny(ll a,ll b){
35     ll x,y,gc=extgcd(x,y,a,b),mo=b/gc;
36     x=(x%mo+mo)%mo;
37     return x;
38 }
39 
40 void makep(){
41     p[0]=r[0]=0;
42     for (ll i=1,x=m;i<=pri[0] && x>1;i++)
43     if (!(x%pri[i])){
44         p[++p[0]]=pri[i];
45         r[++r[0]]=1;
46         while (!(x%pri[i])) x/=pri[i],r[r[0]]*=pri[i];
47     }
48 }
49 void makesave(ll t){
50     save[0]=1;
51     for (ll i=1,x=1;i<=r[t];i++){
52         if (i%p[t]) x=x*i%r[t];
53         save[i]=x;
54     }
55 }
56 ll jc(ll n,ll t,ll &z,ll bo){
57     if (!n) return 1;
58     z+=bo*(n/p[t]);
59     return mi(save[r[t]],n/r[t],r[t])*save[n%r[t]]%r[t]*jc(n/p[t],t,z,bo)%r[t];
60 }
61 ll getc(){
62     makep();
63     for (ll i=1;i<=p[0];i++){
64         zs[i]=0;
65         makesave(i);
66         a[i]=jc(n,i,zs[i],1)*getny(jc(n-k,i,zs[i],-1)*jc(k,i,zs[i],-1)%r[i],r[i])%r[i];
67         a[i]=a[i]*mi(p[i],zs[i],r[i])%r[i];
68     }
69     ll res=0;
70     for (ll i=1;i<=p[0];i++)
71     res=(res+m/r[i]*getny(m/r[i],r[i])%m*a[i]%m)%m;
72     return res;
73 }
74 
75 ll geth(ll n){
76     if (n==1) return 0;
77     ll res=(n&1) ? -1 : 1;
78     for (ll i=n,x=n*res*-1%m;i>=3 && x;x=x*(--i)*-1%m)
79     res=((res+x)%m+m)%m;
80     return res;
81 }
82 
83 int main(){
84     scanf("%I64d",&t);
85     makepri();
86     for (ll i=1;i<=t;i++){
87         tot=i;
88         scanf("%I64d%I64d%I64d",&n,&k,&m);
89         ans=(getc()*geth(n-k)%m+m)%m;
90         printf("Case %I64d: %I64d\n",i,ans);
91     }
92 }
View Code

注:这个代码能过hdu的数据但是不能过spoj的... 可能有点小bug- -?(我说spoj → →)

posted @ 2013-10-21 21:06  g_word  阅读(464)  评论(0编辑  收藏  举报