【GDOI 2011 DAY2 T3】零什么的最讨厌了 (快速求阶乘、中国剩余定理)

问题描述:

林记在做数学习题的时候,经常遇到这种情况:苦思冥想了很久终于把问题解出来,结果发现答案是0,久而久之林记在得到习题答案是0的时候就没有了做出一道难题的成就感。于是林记决定:以后出题,答案一定不能是0,例如求n!最低位非零数这样的习题就很不错了。

现在林记提出了一个更难一点的问题:求n!在K进制下的最低位非零数。其中K符合一些特殊的条件:K是由若干个互不相同的质数相乘得出来的,例如K=2,3,5,6,7,10……

输入格式:

首先输入的第一行是一个整数Q,表示询问的个数。

接下来是Q个询问,每个询问有两行组成,第一行首先是一个整数nk,然后紧跟着nk个正整数:m1m2,……mnk,则K为所有mi的乘积,输入保证mi是有若干个互不相同的质数相乘得出来的。接下来一行给出一个整数n

输出格式:

对于每个询问,如果K不符合题目限制,则输出“I hate zero.”(不用加双引号)。否则输出K进制下n!的最低位非零数。

输入样例:

输出样例:

3

1 17

9

1 6

49

1 93

16

15

2

78

数据范围:

对于10%的数据满足:n10K100

对于50%的数据满足:n100000K100

对于100%的数据满足:n1015K1015mi106Q20

 

 

【分析】

  快速求阶乘大法!!

  下面某步还用到 威尔逊定理 (p-1)!%p=p-1...... (也可以预处理出来的)

  把m分解质因数,对于质数p单独求n!=p^a+b的a的值和b%p的值(logn递归搞定),然后用中国剩余定理合并起来。

  

  long long的乘积会爆,所以...要用那个不断乘2的方法:

  

LL mul(LL x,LL y,LL p)
{
	if(x==0||y==0) return 0;
	LL now=x,yy=y,add=0;
	while(yy>1)
	{
		if(yy%2!=0) add=(add+now)%p;
		now=(now*2)%p;
		yy/=2;
	}
	return (now+add)%p;
}

 

 

代码如下:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 #include<cmath>
  8 using namespace std;
  9 #define Maxn 1000010
 10 #define INF 0xfffffff
 11 #define LL long long
 12 
 13 LL nk,m[Maxn],ml,K;
 14 LL n;
 15 LL prime[Maxn],pl;
 16 bool q[Maxn];
 17 LL mn;
 18 
 19 LL mymin(LL x,LL y) {return x<y?x:y;}
 20 
 21 void get_pri(LL mx)
 22 {
 23     pl=0;
 24     memset(q,1,sizeof(q));
 25     for(LL i=2;i<=mx;i++)
 26     {
 27         if(q[i]) prime[++pl]=i;
 28         for(int j=1;j<=pl;j++)
 29         {
 30             if(i*prime[j]>mx) break;
 31             q[i*prime[j]]=0;
 32             if(i%prime[j]==0) break;
 33         }
 34     }
 35 }
 36 
 37 bool div(LL mm)
 38 {
 39     for(LL i=1;prime[i]*prime[i]<=mm;i++) if(mm%prime[i]==0)
 40     {
 41         m[++ml]=prime[i];
 42         mm/=prime[i];
 43         while(mm%prime[i]==0) return 0;
 44     }
 45     if(mm!=1) m[++ml]=mm;
 46     return 1;
 47 }
 48 
 49 bool init()
 50 {
 51     ml=0;
 52     scanf("%lld",&nk);
 53     K=1;
 54     bool ok=1;
 55     for(int i=1;i<=nk;i++) 
 56     {
 57         LL mm;
 58         scanf("%lld",&mm);K*=mm;
 59         if(!div(mm)) ok=0;
 60     }
 61     sort(m+1,m+1+ml);
 62     for(LL i=2;i<=ml;i++) if(m[i]==m[i-1]) {ok=0;break;}
 63     scanf("%lld",&n);
 64     if(!ok) return 0;
 65     return 1;
 66 }
 67 
 68 LL f[Maxn];
 69 
 70 struct node{LL x,y;};
 71 node c[Maxn];
 72 
 73 node ffind(LL x,LL p)
 74 {
 75     node ans;
 76     if(x<p)
 77     {
 78         ans.x=0;
 79         ans.y=f[x];
 80         return ans;
 81     }
 82     ans.x=0;ans.y=1;
 83     node t=ffind(x/p,p);
 84     ans.x+=t.x; ans.y*=t.y;
 85     ans.y=(ans.y*f[x%p])%p;
 86     ans.x+=x/p;
 87     
 88     LL y=(((x/p)%2)==0)?1:p-1;
 89     ans.y=(ans.y*y)%p;
 90     
 91     return ans;
 92 }
 93 
 94 LL mul(LL x,LL y,LL p)
 95 {
 96     if(x==0||y==0) return 0;
 97     LL now=x,yy=y,add=0;
 98     while(yy>1)
 99     {
100         if(yy%2!=0) add=(add+now)%p;
101         now=(now*2)%p;
102         yy/=2;
103     }
104     return (now+add)%p;
105 }
106 
107 LL qpow(LL x,LL b,LL p)
108 {
109     LL ans=1;
110     while(b)
111     {
112         if(b&1) ans=mul(ans,x,p);
113         x=mul(x,x,p);
114         b>>=1;
115     }
116     return ans;
117 }
118 
119 LL d[Maxn];
120 
121 LL get_ans()
122 {
123     mn=c[1].x;
124     for(LL i=2;i<=ml;i++) mn=mymin(mn,c[i].x);
125     for(LL i=1;i<=ml;i++)
126     {
127         if(c[i].x>mn) {d[i]=0;continue;}
128         LL bm=qpow(K/m[i],mn,m[i]);
129         bm=qpow(bm,m[i]-2,m[i]);
130         d[i]=mul(bm,c[i].y,m[i]);//(bm*c[i].y)%m[i];
131     }
132     LL ans=0;
133     for(LL i=1;i<=ml;i++)
134     {
135         LL y=qpow(K/m[i],m[i]-2,m[i]);
136         //ans=(ans+((K/m[i]*y)%K)*d[i])%K;
137         ans=(ans+mul(mul(K/m[i],y,K),d[i],K))%K;
138     }
139     return ans;
140 }
141 
142 int main()
143 {
144     get_pri(1000000);
145     int T;
146     scanf("%d",&T);
147     while(T--)
148     {
149         if(!init()) {printf("I hate zero.\n");continue;}
150         for(LL i=1;i<=ml;i++)
151         {
152             f[0]=1;
153             for(LL j=1;j<m[i];j++) f[j]=(f[j-1]*j)%m[i];
154             c[i]=ffind(n,(LL)m[i]);
155         }
156         printf("%lld\n",get_ans());
157     }
158     return 0;
159 }
View Code

 

 

2016-09-04 19:37:26

转载于:https://www.cnblogs.com/Konjakmoyu/p/5840039.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值