BZOJ2242 SDOI2011 计算器
Posted Fenghr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。
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
这道题就是一道板子合集。
发现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; }
以上是关于BZOJ2242 SDOI2011 计算器的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ_2242_[SDOI2011]计算器_快速幂+扩展GCD+BSGS