BZOJ2242: [SDOI2011]计算器
2242: [SDOI2011]计算器
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1278 Solved: 492
[Submit][Status]
Description
你被要求设计一个计算器完成以下三项任务:
1、给定y,z,p,计算Y^Z Mod P 的值;
2、给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数;
3、给定y,z,p,计算满足Y^x ≡ Z ( mod P)的最小非负整数。
Input
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
Output
对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”,注意逗号与“I”之间有一个空格。
Sample Input
【样例输入1】
3 1
2 1 3
2 2 3
2 3 3
【样例输入2】
3 2
2 1 3
2 2 3
2 3 3
【数据规模和约定】
对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
3 1
2 1 3
2 2 3
2 3 3
【样例输入2】
3 2
2 1 3
2 2 3
2 3 3
【数据规模和约定】
对于100%的数据,1<=y,z,p<=10^9,为质数,1<=T<=10。
Sample Output
【样例输出1】
2
1
2
【样例输出2】
2
1
0
2
1
2
【样例输出2】
2
1
0
HINT
Source
题解:
第一问快速幂,第二问拓展欧几里德,第三问shank的大步小步算法。
算法基于这样的性质:
若有 a^(k*m+i)=b (mod p)
则有 a^i=b/(a^km) =b*(a^km)-1 (mod p) 其中-1 指 模p意义下的逆元
如果 左边的i很少,我们就可以预处理出所有的 a^i,然后枚举k查询是否有 b*(a^km)-1这样的键值存在,存在的话答案就是 k*m+i.
显然 m取sqrt(n)比较合适。
枚举k时还有技巧就是a^km的逆元随着k的增加,不用每次都求,我们先求出a^m的逆元 t=power(a,p-m-1,p)
这样每次k++,b*=t,这是因为 a^(p-m-1+p-m-1)=a^(p-2*m-1)*a^(p-1)=a^(p-m-1)。
查询可以用map存储,也可以手写二分。
bzoj的评测机太感人了,本地测50s+,在bzoj上居然1s就跑出来了,真是奇迹。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 500+100 14 #define maxm 500+100 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define for0(i,n) for(int i=0;i<=(n);i++) 19 #define for1(i,n) for(int i=1;i<=(n);i++) 20 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 21 using namespace std; 22 typedef map<int,int>::const_iterator cit; 23 typedef map<int,int>::value_type vt; 24 map<int,int> mp; 25 inline ll read() 26 { 27 int x=0,f=1;char ch=getchar(); 28 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 29 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 30 return x*f; 31 } 32 ll a,b,p,x,y,t,tt,gcd,m,cs,ch; 33 ll work1(ll a,ll b,ll p) 34 { 35 ll ans=1; 36 for(;b;b>>=1,a*=a,a%=p) 37 if(b&1)ans*=a,ans%=p; 38 return ans; 39 } 40 void exgcd(ll a,ll b) 41 { 42 if(!b){x=1;y=0;gcd=a;} 43 else 44 { 45 exgcd(b,a%b); 46 t=x;x=y;y=t-(a/b)*y; 47 } 48 } 49 void work2() 50 { 51 exgcd(a,p); 52 if(b%gcd)puts("Orz, I cannot find x!"); 53 else 54 { 55 x*=b/gcd;y*=b/gcd; 56 t=p/gcd; 57 x=(x%t+t)%t; 58 printf("%lld\n",x); 59 } 60 } 61 void work3() 62 { 63 a%=p;b%=p; 64 if(!a&&!b){printf("1\n");return;} 65 if(!a){puts("Orz, I cannot find x!");return;} 66 m=floor(sqrt(p)); 67 mp.clear(); 68 mp.insert(mp.begin(),vt(1,0)); 69 t=1; 70 for1(i,m-1)t*=a,t%=p,mp.insert(mp.begin(),vt(t,i)); 71 t=work1(a,p-m-1,p); 72 for0(i,p/m) 73 { 74 cit j=mp.find(b); 75 if(j!=mp.end()) 76 { 77 printf("%lld\n",j->second+i*m); 78 return; 79 } 80 b*=t;b%=p; 81 } 82 puts("Orz, I cannot find x!"); 83 } 84 int main() 85 { 86 freopen("input.txt","r",stdin); 87 freopen("output.txt","w",stdout); 88 cs=read();ch=read(); 89 while(cs--) 90 { 91 a=read();b=read();p=read(); 92 switch(ch) 93 { 94 case 1:printf("%lld\n",work1(a,b,p));break; 95 case 2:work2();break; 96 case 3:work3();break; 97 } 98 } 99 return 0; 100 }