BZOJ2242 SDOI2011 计算器calc

第一类询问:直接快速幂不会的右转百度

第二类询问:求个逆元再判一下不会的右转百度

第三类询问:

首先有费马小定理、、所以答案不会超过p、、

但是p的范围还是要T、、

于是想到二分或者分块、、、

二分的话感觉没什么前途、、

于是分块、、

(我不会打同余标记就用等于代替了、、)

设最终答案x=k[sqrt(p)]+i

那么有

y^x=z(mod p)

y^(k[sqrt(p)]+i)=z(mod p)

y^i=z/(y^k[sqrt(p)]) (mod p)

y^i=z*y^(k[sqrt(p)](p-2)) (mod p) (费马小定理)

这里i的范围是0-[sqrt(p)]-1的、、k的范围是0..P/[sqrt(p)]的、、

然后我们可以预处理出y^i存在hash表或者map里、、

然后枚举k看是否存在这样一个i、、

于是就可以解决了、、详细的可以见code、、

 

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <queue>

#define ps system("pause")
#define message printf("*\n")
#define pb push_back
#define X first
#define Y second
#define PII pair<int,int>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)

typedef long long ll;

using namespace std;

int T,k;
ll y,z,p,te,ans,c;
map<ll,ll> app;

int main(){
	cin >>T >>k;
	while	(T--){
		cin >>y >>z >>p;
		app.clear();
		switch	(k){
			case	1:
				te=y;ans=1;
				while	(z){
					if	(z&1)	ans=ans*te%p;
					(te*=te)%=p;z/=2;
				}
				cout <<ans <<endl;
				break;
			case	2:
				c=p-2;te=y;ans=1;
				while	(c){
					if	(c&1)	ans=ans*te%p;
					(te*=te)%=p;c/=2;
				}
				ans=z*ans%p;
				if	((ans*y%p)==(z%p))	cout <<ans <<endl;
				else	puts("Orz, I cannot find x!");
				break;
			case	3:
				if	(y%p==0){
					puts("Orz, I cannot find x!");
					break;
				}
				te=1;
				rep(i,0,int(sqrt(p))){
					app[te]=i+1;
					te=te*y%p;
				}
				te=int(sqrt(p));
				bool fd=false;
				rep(k,0,p/te){
					c=k*(p-2)*te;
					ll cur=y,res=1;
					while	(c){
						if	(c&1)	res=res*cur%p;
						(cur*=cur)%=p;c/=2;
					}
					res=res*z%p;
					if	(app[res]){
						fd=true;
						cout <<k*te+app[res]-1 <<endl;
						break;
					}
				}
				if	(!fd)	puts("Orz, I cannot find x!");
				break;
		}
	}
	return	0;
}

  

 

你可能感兴趣的:(计算器)