BZOJ2242 SDOI2011 计算器

2242: [SDOI2011]计算器

Time Limit: 10 Sec  Memory Limit: 512 MB

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
 
  这道题就是一道板子合集。
  发现B站样例没有操作3,那么这就很难受。
  但是洛谷和codevs上面有。
  对于操作1,NOIP提高组D1T1难度。
  
  对于操作2,因为有 XY≡Z,由数论姿势可以得到
    X*Y*Y-1≡X≡Z*Y-1(mod p);
  Y-1是指Y的逆元。因为题目说了P为素数,用欧拉定理就可以了。
  对于操作3,离散数对离散对数板子题。
  这一类做法就是套路分块。
  设x=i*m+j。则有:
    Y(i*m+j)≡Z;
  即 Yi*m≡Z*Y-j
  枚举i,则Yi*m可以算出来。假设为K。
  于是就有:
    Z*Y-j≡K (mod p)
  然后像第二问一样把Y-j求出来,看一下[0,m)内有没有解。
  这个东西可以用一个哈希表或者map,set什么的预处理[0,m)的逆元来搞搞。
  所以说m选在√n的时候是最优秀的。
 
  然后因为某很懒,不愿意想太多事,所以把这个算法懒化了一下。
  (这么干的某不是第一个。)
  设x=i*m-j;
  没错最后的式子就是
    Z*Yj≡K (mod p);
  一个小小的改动,就可以不用找逆元了。
  
  最后提一下判断无解的点:
    对于操作2,如果Y=0而Z!=0就一定无解。
    对于操作3,如果Y=0而Z!=0就一定无解。
    对于操作3,找到最后找不到了就是无解了。
  然后我就想知道B站那些时间只要三位数的老爷是什么情况。
  可能操作2用exgcd更优秀吧,真的吗?
  某的3000ms完全没戏啊,应该是卡不下来的?
#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#include    <cmath>
#define LL long long int
#define dob double
using namespace std;
 
const int N = 1000010;
struct Node{int to,val,next;}E[N];
int n,type,head[N],tot;
 
int gi()
{
  int x=0,res=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
  while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  return x*res;
}
 
inline int QPow(int d,int z,int Mod){
  int ans=1;
  for(;z;z>>=1,d=1ll*d*d%Mod)
    if(z&1)ans=1ll*ans*d%Mod;
  return (ans+Mod)%Mod;
}
 
inline void link(int u,int v,int w){
  E[++tot]=(Node){v,w,head[u]};
  head[u]=tot;
}
 
inline void work1(){
  while(n--){
    int y=gi(),z=gi(),p=gi();
    printf("%d\n",QPow(y%p,z%(p-1),p));
  }
  return;
}
 
inline void work2(){
  while(n--){
    int y=gi(),z=gi(),p=gi();
    y%=p;z%=p;
    if(!y && z){
      printf("Orz, I cannot find x!\n");
      continue;
    }
    int x=QPow(y,p-2,p);
    x=1ll*x*z%p;
    printf("%d\n",x);
  }
  return;
}
 
inline void work3(){
  while(n--){
    int y=gi(),z=gi(),p=gi();
    y%=p;z%=p;
    if(!y && z){
      printf("Orz, I cannot find x!\n");
      continue;
    }      
    int m=sqrt(p),flag=0;
    memset(head,0,sizeof(head));tot=0;
    for(int i=0,d=1;i<m;++i){
      link(d%N,d,i);
      d=1ll*d*y%p;
    }
    for(int i=1;i<=m && !flag;++i){
      int ny=QPow(y,i*m,p);
      int x=QPow(z,p-2,p);
      x=1ll*x*ny%p;
      for(int e=head[x%N];e;e=E[e].next)
        if(E[e].to==x){
          printf("%d\n",(i*m-E[e].val)%p);
          flag=1;break;
        }
    }
    if(!flag)
      printf("Orz, I cannot find x!\n");
  }
}
 
int main()
{
  n=gi();type=gi();
  if(type==1)work1();
  if(type==2)work2();
  if(type==3)work3();
  /*fclose(stdin);
    fclose(stdout);*/
  return 0;
}

  

  
posted @ 2017-07-31 08:11  Fenghr  阅读(241)  评论(2编辑  收藏  举报