BZOJ2242: [SDOI2011]计算器

2242: [SDOI2011]计算器

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 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。

Sample Output

【样例输出1】
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 }
View Code

 

posted @ 2014-09-07 21:35  ZYF-ZYF  Views(314)  Comments(0Edit  收藏  举报