BZOJ2242 [SDOI2011]計算器 快速冪+Exgcd+離散對數 數學專題第十題

~hsm~發表於2019-03-26

題目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的一種運用
yx=z(mod p)y^x=z(mod\text{ } p)x=km+ix=km+i
ykm  yizy^{km}\text{ }∗\text{ }y^i≡z
yiz  inv(ykm)()y^i≡z\text{ }∗\text{ }inv(y^{km})(逆元)
用費馬小定理顯然可得inv(ym)yp1minv(y^m)≡y^{p−1−m}設其為TT
inv (ykm)inv (y(k1)m)  Tinv\text{ }(y^{km})≡inv\text{ }(y^{(k−1)m})\text{ }∗\text{ }T

yi(0<=i<=m)y^i(0<=i<=m)
放入hash或者map
然後列舉k,查詢
z  inv(ykm)z\text{ }∗\text{ }inv(y^{km})
顯然mmp√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;
}

相關文章