FZOJ#181. 【2017FZSZ寒假集训-DAY10】告
【问题描述】
定义 S 为, 对于所有可以整除 n 的整数 k(包括 1 和n ) , C(n,k)之和. 求 GS 除以 999911659 的余数.
【输入格式】
共一行, 两个整数 n , G .
【输出格式】
一个整数, 如题所述.
【样例输入】4 2
【样例输入】2048
【样例解释】
k=1,2,4
S=C(4,1)+C(4,2)+C(4,4)=11
Gs=211=2048
【数据规模】
对于 10 的数据, 1≤n≤50。
对于 20 的数据, 1≤n≤103 。
对于 40 的数据, 1≤n≤105 。
对于 100 的数据, 1≤G≤109 , 1≤n≤109 。
【时间限制】1s
【空间限制】256M
【题解】
看到这题首先想到的就是lucas,然而发现是GS不能直接模99991165。 根据费马小定理,我们可以把S对于999911658取模。然而还是太大了。我们发现这时候我们需要的模数已经不是一个质数了,所以我们可以运用中国剩余定理。接下来给出中国剩余定理的证明。
对于如上图这样的一个式子,我们就会用到中国剩余定理。
我们设一个数Yi。对于每一个Yi都满足Yi ≡ 1(mod mi)且 Yi ≡ 0(mod mj)(j ≠ i)。
现在我们设M= ,N,K为任意常数。
根据 Yi 的定义,我们可以知道下面这两个式子
Yi = mi × N + 1
Yi = M / mi × K
联立两个式子可得 mi × N + 1 = M / mi × K
整理得 -mi× N+M / mi × K = 1
又因为mi都是质数,所以gcd( -mi,M / mi )=1
所以 -mi× N+M / mi × K = gcd( -mi,M / mi )
然后我们就可以用exgcd来求解啦。
S = (M/mi) ×ai ×K i
【代码】
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; #define LL long long LL n,G; LL mod=999911659,fac[5][40001]; LL mm[5]={999911659,2,3,4679,35617},a[5]={}; void init(){ fac[1][0]=fac[2][0]=fac[3][0]=fac[4][0]=1; for(int j=1;j<=4;j++){ for(int i=1;i<=40000;i++) fac[j][i]=fac[j][i-1]*(LL)i%mm[j]; } } LL power(LL x,LL y,LL now){ LL r=1; x%=mm[now]; while(y){ if(y&1) r=(r*x)%mm[now]; x=(x*x)%mm[now];y>>=1; } return r; } LL C(LL n,LL m,LL now){ if(n==m) return 1LL; if(m>n) return 0; return (fac[now][n]*power((fac[now][n-m]*fac[now][m])%mm[now],mm[now]-2,now))%mm[now]; } LL lucas(LL n,LL m,LL now){ if(!m) return 1; return (C(n%mm[now],m%mm[now],now)*lucas(n/mm[now],m/mm[now],now))%mm[now]; } void gcd(LL a,LL b,LL &x,LL &y){ if(!b){ x=1;y=0;return; } gcd(b,a%b,x,y); LL tmp=x;x=y;y=tmp-a/b*y; } LL crt(LL a,LL b,LL c,LL d){ LL r[5]={0,a,b,c,d}; LL p=mod-1,ans=0; for(int i=1;i<=4;i++){ LL mi=p/mm[i],v=0,y=0; gcd(mi,mm[i],v,y); ans=(ans+v*mi*r[i])%p; } return (ans+p)%p; } void work(LL x){ LL modn; for(int i=1;i<=4;i++){ modn=mm[i]; a[i]+=lucas(n,x,i); a[i]%=modn; } } int main(){ init(); scanf("%lld%lld",&n,&G); if(G==mod){ puts("0");return 0; } G%=mod; for(int i=1;i*i<=n;i++){ if(n%i==0){ work(i); if(i*i!=n) work(n/i); } } LL sum=crt(a[1],a[2],a[3],a[4]); printf("%lld",power(G,sum,0LL)); return 0; }
浙公网安备 33010602011771号