2242: [SDOI2011]计算器

2242: [SDOI2011]计算器

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 3045   Solved: 1206
[ Submit][ Status][ Discuss]

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

[ Submit][ Status][ Discuss]

对于询问1,快速幂解决
对于询问2,扩展欧几里得解决
对于询问3,采用BSGS算法
询问y^x≡z(mod p)的最小非负整数解x,保证p为素数
令Sqrt = sqrt(p)
则y^x = (y^Sqrt)^i*y^j
枚举i,令T = (y^Sqrt)^i
则原式化为T*y^j≡z(mod p)
用扩展欧几里得求出y^j
hash查表。。。

如果p不是素数,可以用扩展BSGS
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int hash = 999983;
typedef long long LL;

int T,typ,top,s[hash],ha[hash],va[hash];

int ksm(LL x,int y,LL p)
{
	LL ret = 1;
	for (; y; y >>= 1) {
		if (y & 1) ret = ret*x%p;
		x = x*x%p;
	}
	return ret;
}

LL gcd(LL a,LL b,LL &x,LL &y)
{
	if (!b) {
		x = 1; y = 0;
		return a;
	}
	LL ret = gcd(b,a%b,x,y);
	LL tmp = x;
	x = y;
	y = tmp - y*(a/b);
	return ret;
}

LL GCD(LL a,LL b,LL z)
{
	LL x,y;
	LL g = gcd(a,b,x,y);
	if (z % g != 0) return -1;
	x = x * z / g;
	if (x < 0) 
		x = x % b + b;
	return x % b;
}

void hash_insert(int v,int mi)
{
	int posi = v%hash;
	while (va[posi] != -1) {
		if (va[posi] == v) return;
		++posi;
	}
	va[posi] = v; ha[posi] = mi;
	s[++top] = posi;
}

int hash_search(LL now)
{
	int posi = now%hash;
	for (;;) {
		if (va[posi] == -1) return -1;
		if (va[posi] == now) return ha[posi];
		++posi;
	}
}

LL Baby_step(LL t,LL p,LL z,LL Sqrt)
{
	LL now = 1;
	for (int i = 0; i <= Sqrt; i++) {
		LL x = GCD(now,p,z);
		if (x == -1) continue;
		int posi = hash_search(x%p);
		if (posi != -1) 
			return i*Sqrt + posi;
		now = now*t%p;
	}
	return -1;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	memset(va,-1,sizeof(va));
	cin >> T >> typ;
	while (T--) {
		if (typ == 1) {
			int x,y,p;
			cin >> x >> y >> p;
			printf("%d\n",ksm(x,y,p));
		}
		else if (typ == 2) {
			int y,z,p; LL x;
			cin >> y >> z >> p;
			x = GCD(y,p,z);
			if (x == -1) puts("Orz, I cannot find x!");
			else cout << x << endl;
		}
		else {
			int y,z,p,Sqrt,t; LL x;
			cin >> y >> z >> p;
			Sqrt = sqrt(p);
			if (Sqrt * Sqrt < p) ++Sqrt;
			int now = 1; //top = 0;
			for (int i = 0; i <= Sqrt; i++) 
				hash_insert(now,i),now = 1LL*now*y%p;
			t = ksm(y,Sqrt,p);
			x = Baby_step(t,p,z,Sqrt);
			if (x == -1) puts("Orz, I cannot find x!");
			else cout << x << endl;
			while (top) va[s[top--]] = -1;
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值