BZOJ2242 [SDOI2011]计算器 快速幂+Exgcd+离散对数 数学专题第十题

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/86538518

题目p2109

BZOJ 2242
洛谷地址传送门

From Tyvj Guest☆【bzoj2242】计算器
背景 Background
此系列问题为数论专题
具体参考博文
http://blog.csdn.net/chty2018/article/details/53432272
描述 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 Format
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
输出格式 Output Format
对于每个询问,输出一行答案。对于询问类型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
【样例输入3】
4 3
2 1 3
2 2 3
2 3 3
2 4 3
样例输出 Sample Output
【样例输出1】
2
1
2
【样例输出2】
2
1
0
【样例输出3】
0
1
Orz, I cannot find x!
0
时间限制 Time Limitation
1s
注释 Hint
【数据规模和约定】
对于20%的数据,K=1;
对于35%的数据,K=2;
对于45%的数据,K=3;
对于100%的数据,1≤y,z,P≤〖10〗^9,P为质数,1≤T≤10。
来源 Source
【bzoj2242】计算器
题面,数据来自宋逸群

题解

第一问快速幂
第二问exgcd
第三问BSGS,好麻烦我就先略
为啥叫baby step giant step,我其实不是很懂
卓神说这是meet in the middle的一种运用
y x = z ( m o d   p ) y^x=z(mod\text{ } p) x = k m + i x=km+i
y k m     y i z y^{km}\text{ }∗\text{ }y^i≡z
y i z     i n v ( y k m ) ( ) y^i≡z\text{ }∗\text{ }inv(y^{km})(逆元)
用费马小定理显然可得 i n v ( y m ) y p 1 m inv(y^m)≡y^{p−1−m} 设其为 T T
i n v   ( y k m ) i n v   ( y ( k 1 ) m )     T inv\text{ }(y^{km})≡inv\text{ }(y^{(k−1)m})\text{ }∗\text{ }T

y i ( 0 < = i < = m ) y^i(0<=i<=m)
放入hash或者map
然后枚举k,查询
z     i n v ( y k m ) z\text{ }∗\text{ }inv(y^{km})
显然 m m p √p 复杂度比较优秀。。
转自hzwer

然而我现在还是不会离散对数。

代码

这个学习黄学长的代码的评测结果:

Result Memory Time Language Code_Length Submit_Time
Accepted 3288 kb 2012 ms C++/Edit 2158 B 2019-01-18 16:23:49
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
	int f=1,x=0;
	char ch=getchar();
	while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	return x*f;
}
/////////////////////////////////////////////////////////////////////////////
//快速幂
int solve1(ll y,int z,int p)
{
	y%=p;
	ll ans=1;
	for (int i=z; i; i>>=1,y=y*y%p)
		if (i&1)
			ans=ans*y%p;
	return ans;
}
/////////////////////////////////////////////////////////////////////////////
//Exgcd
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
void exgcd(int a,int b,int &x,int &y)
{
	if (!b)
	{
		x=1,y=0;
		return;
	}
	exgcd(b,a%b,x,y);
	int t=x;
	x=y;
	y=t-a/b*y;
}
void solve2(int y,int z,int p)
{
	p=-p;
	int t=gcd(y,p);
	if (z%t)
	{
		puts("Orz, I cannot find x!");
		return ;
	}
	y/=t,z/=t,p/=t;
	int a,b;
	exgcd(y,p,a,b);
	a=(ll)a*z%p;
	while (a<0) a+=p;
	printf("%d\n",a);
}
/////////////////////////////////////////////////////////////////////////////
//离散对数
map<int,int>mp;
void solve3(int y,int z,int p)
{
	y%=p;
	if (!y && !z)
	{
		puts("1");
		return ;
	}
	if (!y)
	{
		puts("Orz, I cannot find x!");
		return ;
	}
	mp.clear();
	ll m=ceil(sqrt(p)),t=1;
	mp[1]=m+1;
	for (ll i=1; i<m; ++i)
	{
		t=t*y%p;
		if (!mp[t])
			mp[t]=i;
	}
	ll tmp=solve1(y,p-m-1,p),inv=1;
	for (ll k=0; k<m; ++k)
	{
		int i=mp[z*inv%p];
		if (i)
		{
			if (i==m+1) i=0;
			printf("%lld\n",k*m+i);
			return;
		}
		inv=inv*tmp%p;
	}
	puts("Orz, I cannot find x!");
}
/////////////////////////////////////////////////////////////////////////////
int main()
{
	int T=read(),K=read();
	while (T--)
	{
		int y=read(),z=read(),p=read();
		if (K==1)
			printf("%d\n",solve1(y,z,p));
		else if (K==2)
			solve2(y,z,p);
		else
			solve3(y,z,p);
	}
	return 0;
}

下面的代码转自大神yyb

Result Memory Time Language Code_Length Submit_Time
Accepted 13408 kb 152 ms C++/Edit 2297 B 2019-01-18 14:41:52
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
const int HashMod=100007;
inline int read() {
	RG int x=0,t=1;
	RG char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') t=-1,ch=getchar();
	while (ch<='9'&&ch>='0') x=x*10+ch-48,ch=getchar();
	return x*t;
}
int fpow(int a,int b,int MOD) {
	int s=1;
	while (b) {
		if (b&1)
			s=1ll*s*a%MOD;
		a=1ll*a*a%MOD;
		b>>=1;
	}
	return s;
}
void NoAnswer() {
	puts("Orz, I cannot find x!");
}
namespace Task1 {
	void Solve(int y,int z,int p) {
		printf("%d\n",fpow(y,z,p));
	}
}
namespace Task2 {
	void Solve(int y,int z,int p) {
		if (y%p==0&&z%p)
			NoAnswer();
		else
			printf("%lld\n",1ll*fpow(y,p-2,p)*z%p);
	}
}
namespace Task3 {
	struct HashTable {
		struct Line {
			int u,v,next;
		} e[1000000];
		int h[HashMod],cnt;
		void Add(int u,int v,int w) {
			e[++cnt]=(Line) {
				w,v,h[u]
			};
			h[u]=cnt;
		}
		void Clear() {
			memset(h,0,sizeof(h));
			cnt=0;
		}
		void Hash(int x,int k) {
			int s=x%HashMod;
			Add(s,k,x);
		}
		int Query(int x) {
			int s=x%HashMod;
			for (int i=h[s]; i; i=e[i].next)
				if (e[i].u==x)
					return e[i].v;
			return -1;
		}
	} Hash;
	void Solve(int y,int z,int p) {
		if (y%p==0) {
			NoAnswer();
			return;
		}
		y%=p;
		z%=p;
		if (z==1) {
			puts("0");
			return;
		}
		int m=sqrt(p)+1;
		Hash.Clear();
		for (RG int i=0,t=z; i<m; ++i,t=1ll*t*y%p)
			Hash.Hash(t,i);
		for (RG int i=1,tt=fpow(y,m,p),t=tt; i<=m+1; ++i,t=1ll*t*tt%p) {
			int k=Hash.Query(t);
			if (k==-1) continue;
			printf("%d\n",i*m-k);
			return;
		}
		NoAnswer();
	}
}
int main() {
	int T=read(),K=read();
	while(T--) {
		int y=read(),z=read(),p=read();
		if (K==1) Task1::Solve(y,z,p);
		if (K==2) Task2::Solve(y,z,p);
		if (K==3) Task3::Solve(y,z,p);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/86538518