数论习题

扩展欧几里得的应用
求解同余方程
a x + b y = m ax+by=m ax+by=m x x x 的最小值,即求解同余方程 a x ≡ m ( m o d   b ) ax\equiv m(mod\ b) axm(mod b)的最小正整数解
分析:通过扩展欧几里得算法可以求得 a x 1 + b y 1 = g c d ( a , b ) ax_1+by_1=gcd(a,b) ax1+by1=gcd(a,b)的其中一个解 x 1 x_1 x1。但是这个 x 1 x_1 x1需要乘上 m g c d ( a , b ) \frac m{gcd(a,b)} gcd(a,b)m才是 a x + b y = m ax+by=m ax+by=m这个方程的解。即 x ′ = x 1 × m g c d ( a , b ) x'=x_1\times \frac m{gcd(a,b)} x=x1×gcd(a,b)m。但此时的 x ′ x' x仍然不是最小的答案。
我们可以将 a x ′ + b y ′ = m ax'+by'=m ax+by=m,同时除以 g c d ( a , b ) gcd(a,b) gcd(a,b),得到 a 1 x ′ + b 1 y ′ = m 1 a_1x'+b_1y'=m_1 a1x+b1y=m1,变形得到 a 1 ( x ′ + k b 1 ) + b 1 ( y ′ − k a 1 ) = m 1 a_1(x'+kb_1)+b_1(y'-ka_1)=m_1 a1(x+kb1)+b1(yka1)=m1,我们的目标是使 x ′ + k b 1 x'+kb_1 x+kb1最小,因此最后的答案就是 x ′ % b 1 x'\%b_1 x%b1
总结一下求解同余方程的步骤
1、首先判断m是否整除gcd(a,b),不整除则不存在解
证明:因为 g c d ( a , b ) ∣ a , g c d ( a , b ) ∣ b gcd(a,b)|a,gcd(a,b)|b gcd(a,b)a,gcd(a,b)b,因此 g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)ax+by
g c d ( a , b ) ∣ m gcd(a,b)|m gcd(a,b)m
2、通过扩展欧几里得算法,求得 x 1 x_1 x1,然后得到所求方程的一个解 x ′ = x 1 × m g c d ( a , b ) x'=x_1\times \frac m{gcd(a,b)} x=x1×gcd(a,b)m
3、最后ans= x ′ % b g c d ( a , b ) x'\%\frac b{gcd(a,b)} x%gcd(a,b)b

青蛙的约会POJ1061

思路:因为两只青蛙跳的时间相同,跳的步数也就相同。设总共走了t步,建立方程 x + m t = y + n t + L p x+mt=y+nt+Lp x+mt=y+nt+Lp
移项得 ( m − n ) t − L p = y − x (m-n)t-Lp=y-x (mn)tLp=yx 将该方程与 a x + b y = m ax+by=m ax+by=m 对应求解即可

#include <iostream>
#define ll long long
using namespace std;
ll x,y,m,n,L;
ll a,b,M,X,Y;

ll gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

int main()
{
	cin>>x>>y>>m>>n>>L;
	a=(m-n);
	b=-L;
	M=y-x;
    ll g=gcd(a,b,X,Y);
    if(M%g!=0)
    {
    	cout<<"Impossible\n";
    	return 0;
    }
    X=X*M/g;
    ll b1=b/g; 
    X=(X%b1+b1)%b1;
    cout<<X<<"\n";
    
	return 0;
}

五指山 NEFU 84

题目描述
我们假设佛祖的手掌是一个圆圈(所以任凭大圣一个筋斗云十万八千里也是飞不出其手掌心),圆圈的长为n,逆时针记为:0,1,2,…,n-1,而大圣每次飞的距离为d.现在大圣所在的位置记为x,而大圣想去的地方在y。现在要你告诉大圣至少要多少筋斗云才能到达目的地

输入
有多组测试数据。
第一行是一个正整数T,表示测试数据的组数。
每组测试数据包括一行,四个非负整数, n ( 2 < n < 1 0 9 ) n(2 < n < 10^9) n2<n<109,表示如来手掌圆圈的长度; d ( 0 < d < n ) d(0 < d < n) d(0<d<n),筋斗所能飞的距离; x ( 0 ≤ x < n ) x(0 \le x < n) x(0x<n),大圣的初始位置; y ( 0 ≤ y < n ) y(0 \le y < n) y(0y<n),大圣想去的地方。
注意孙悟空的筋斗云只沿着逆时针方向翻
输出
对于每组测试数据,输出一行,给出大圣最少要翻多少个筋斗云才能到达目的地。如果无论翻多少个筋斗云也不能到达,输出“Impossible”.

思路:假设孙悟空飞了 k 1 k_1 k1次,建立方程 x + k 1 d = y + k 2 n x+k_1d=y+k_2n x+k1d=y+k2n
移项得: k 1 d − k 2 n = y − x k_1d-k_2n=y-x k1dk2n=yx,将该方程与 a x + b y = m ax+by=m ax+by=m 对应求解

#include <iostream>
#define ll long long
using namespace std;
ll n,d,x,y;
ll a,b,X,Y,M;
int T;

ll gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

int main()
{
	cin>>T;
	while(T--)
	{
		cin>>n>>d>>x>>y;
		a=d;
		b=n;
		M=y-x;
		ll GCD=gcd(a,b,X,Y);
		if(M%GCD!=0)
		{
			cout<<"Impossible\n";
			continue;
		}
		X=X*M/GCD;
		ll b1=b/GCD;
		X=(X%b1+b1)%b1;
		cout<<X<<"\n";
		
	}
	return 0;
}

C Looooops POJ2115

思路:求解 A + k 1 C ≡ B ( m o d   ( 2 k ) ) A+k_1C\equiv B(mod\ (2^k)) A+k1CB(mod (2k))
化简得: A + k 1 C = B + k 2 2 k ⟶ C k 1 − 2 k k 2 = B − A A+k_1C=B+k_2{2^k}\longrightarrow Ck_1-2^kk_2=B-A A+k1C=B+k22kCk12kk2=BA求出 k 1 k_1 k1得值即可

#include <iostream>
#define ll long long
using namespace std;

ll gcd(ll a,ll b,ll &x,ll&y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

int main()
{
	ll a,b,c,k;
	while(cin>>a>>b>>c>>k&&(a||b||c||k))
	{
		ll A,B,X,Y,M;
		A=c;
		B=(1ll<<k);
		M=b-a;
		ll GCD=gcd(A,B,X,Y);
		if(M%GCD!=0)
		{
			cout<<"FOREVER\n";
			continue;
		}
		ll b1=B/GCD;
		X=(X*M/GCD%b1+b1)%b1;
		cout<<X<<"\n";
	}
	return 0;
}

求一个大数的位数的方法
1、 l o g 10 ( n ) + 1 log_{10}(n)+1 log10(n)+1
2、斯特林(Stirling)公式
n ! ≈ 2 π n ( n e ) n n!\approx \sqrt{2\pi n}(\frac ne)^n n!2πn (en)n
n ! n! n!的位数
利用公式 l o g 10 ( n ) + 1 log_{10}(n)+1 log10(n)+1,化简得 l o g 10 ( 2 π n ( n e ) n ) + 1 log_{10}(\sqrt{2\pi n}(\frac ne)^n)+1 log10(2πn (en)n)+1
⟶ 1 2 l o g 10 ( 2 π n ) × n l o g 10 ( n e ) + 1 \longrightarrow \frac 12log_{10}(2\pi n) \times nlog_{10}(\frac ne)+1 21log10(2πn)×nlog10(en)+1
P I ≈ 3.141592654 PI\approx 3.141592654 PI3.141592654
E ≈ 2.71828182846 E \approx 2.71828182846 E2.71828182846

const double PI = acos(-1);
const double E = exp(1);

int Length(int N)
{
    int len=1;
    if(N>3)
    	len=log10(2*PI*N)/2+N*log10(N/E)+1;
    return len;
}

素数分布应用

素数个数的位数 NEFU117

题意:输入n,输出小于 1 0 n 10^n 10n的素数的个数的位数
思路:利用素数定理 π ( x ) = x l n x \pi (x)=\frac x{lnx} π(x)=lnxx和求位数公式 l o g 10 ( n ) + 1 log_{10}(n)+1 log10(n)+1

int main()
{
	int n;
	while(cin>>n)
	{
		int ans=n-log10(n)-log10(log(10))+1;
		cout<<ans<<"\n";		
	}
	return 0;
}

云之遥–素数 NEFU109

题意:判断一个 2 × 1 0 9 2\times 10^9 2×109以内的数是否是素数
思路:利用素数基本判别法,判断 n n n 是否能被 n \sqrt n n 内的素数整除

#include <iostream>
#include <cstring>
#include <string>
#define mes(a,b)  memset(a,b,sizeof(a))
using namespace std;
const int maxn=45000,INF=0x3f3f3f3f;
const int mod=1e9+7;

int prime[maxn],visit[maxn],cnt;

void Prime(int N)
{
	mes(prime,0);
	mes(visit,0);
	visit[0]=visit[1]=1;
	for(int i=2;i*i<N;++i)
	{
		if(!visit[i])
		{
			for(int j=i*i;j<N;j+=i)
				visit[j]=1;
		}
	}
	cnt=0;
	for(int i=2;i<N;++i)
		if(!visit[i])
			prime[++cnt]=i;
}

bool isPrime(int n)
{
	if(n==1||n==0)
		return false;	
	for(int i=1;i<=cnt&&prime[i]*prime[i]<=n;++i)
		if(n%prime[i]==0)
			return false;
	return true;
}

int main()
{
	Prime(maxn);
	int n;
	while(cin>>n)
	{
		if(isPrime(n))
			cout<<"YES\n";
		else
			cout<<"NO\n";
	}
	return 0;
}

Prime Distance POJ2689

题意:给定一个区间 [ L , R ] [L,R] [L,R]求区间中素数的个数。 1 ≤ L < R ≤ 2 31 1\le L < R \le 2^{31} 1L<R231,且L和R的距离不超过 1 × 1 0 6 1\times 10^6 1×106
思路:区间素数筛模板题,首先筛出 [ 1 , 2 31 ] [1,\sqrt {2^{31}}] [1,231 ]范围内的素数,然后用这个范围内的素数,去筛除掉 [ L , R ] [L,R] [L,R]范围内的合数,最后留下来的数,就是素数

细节:
1、 [ 1 , 2 31 ] [1,\sqrt {2^{31}}] [1,231 ]大约是46341,所以只需筛出[1,50000]范围内的素数
2、L和R的距离不超过 1 × 1 0 6 1\times 10^6 1×106,开一个 1 × 1 0 6 1\times 10^6 1×106大小的数组,偏移地存储素数即可
3、极限数据加法、乘法,很容易爆int,直接开ll
4、左区间 l ≤ 2 l\le 2 l2 的时候,直接设置为2。如果不设置为2,那么这个1是筛不掉的,就会把1也当做素数

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=50000,INF=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;

int prime[maxn],visit[maxn],cnt;
int prime2[N],visit2[N],cnt2; 

void Prime(int N)
{
	mes(prime,0);
	mes(visit,0);
	visit[0]=visit[1]=1;
	for(int i=2;i*i<N;++i)
	{
		if(!visit[i])
		{
			for(int j=i*i;j<N;j+=i)
				visit[j]=1;
		}
	}
	cnt=0;
	for(int i=2;i<N;++i)
		if(!visit[i])
			prime[++cnt]=i;
}

void IntervalSieve(ll l,ll r)
{
	mes(visit2,0);
	if(l<2)
		l=2;
	for(int i=1;i<=cnt&&prime[i]*prime[i]<=r;++i)
	{
		ll L=max(2ll,1ll*(l+prime[i]-1)/prime[i])*prime[i];
		for(ll j=L;j<=r;j+=prime[i])
				visit2[j-l+1]=1;
	}
	cnt2=0;
	for(int i=1;i<=r-l+1;++i)
		if(!visit2[i])
			prime2[++cnt2]=i+l-1;
}

int main()
{
	Prime(50000);
	int a,b;
	while(cin>>a>>b)
	{
		IntervalSieve(a,b);
		if(cnt2<2)
			printf("There are no adjacent primes.\n");
		else
		{
			int x1=prime2[1],y1=prime2[2],x2=prime2[1],y2=prime2[2];
			for(int i=3;i<=cnt2;++i)
			{
				if(prime2[i]-prime2[i-1]<y1-x1)
					y1=prime2[i],x1=prime2[i-1];
				if(prime2[i]-prime2[i-1]>y2-x2)
					y2=prime2[i],x2=prime2[i-1];
			}
			printf("%d,%d are closest, %d,%d are most distant.\n",x1,y1,x2,y2);			
		}
	} 
	return 0;
}

算数基本定理的应用

n!后面有多少个0 NEFU118

题意:计算n!末尾0的个数
思路:0是由2和5组成的,显然2的个数远大于5,利用算数基本定理:n!的素因子分解中的素数p的幂为, p a p^a pa
a = ⌊ n p ⌋ + ⌊ n p 2 ⌋ + ⌊ n p 3 ⌋ ⋯ a=\lfloor \frac np\rfloor+\lfloor \frac n{p^2}\rfloor+\lfloor \frac n{p^3}\rfloor \cdots a=pn+p2n+p3n
直接计算5的幂即可

#include <iostream>
using namespace std;
int T,n;

int main()
{
	cin>>T;
	while(T--)
	{
		cin>>n;
		int cur=5;
		int ans=0;
		while(cur<=n)
		{
			ans+=n/cur;
			cur*=5;
		}
		cout<<ans<<"\n";
	}  
	return 0;
}

组合素数 NEFU119

题意:输入n、p,求 C 2 n n C_{2n}^n C2nn中素因子p的个数
细节:注意乘法会爆int

#include <iostream>
#define ll long long
using namespace std;
int T,n,p;

int main()
{
	cin>>T;
	while(T--)
	{
		cin>>n>>p;
		ll cur=p;
		int ans=0;
		while(cur<=n)
		{
			ans+=n/cur;
			cur*=p;
		}
		int ans2=0;
		cur=p;
		while(cur<=2*n)
		{
			ans2+=2*n/cur;
			cur*=p;
		}
		ans=ans2-ans*2;
		if(ans<0)
			ans=0;
		cout<<ans<<"\n";		
	}  
	return 0;
}

Reduced ID Numbers

题意:求一个最小的数m对给出的n个数都不同余
思路:直接从从小到大暴力枚举答案,看每个数对该数的取余值是否相同
细节:memset(visit,0,sizeof(int)*ans),不要全部清空,不然会T

#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e6+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

int T,N,a[maxn],visit[maxn];

int main()
{
	cin>>T;
	while(T--)
	{
		cin>>N;
		rep(i,1,N)
			cin>>a[i];
		int ans=1;
		while(1)
		{
			memset(visit,0,sizeof(int)*ans); 
			bool valid=true;
			for(int i=1;i<=N;++i)
			{
				if(!visit[a[i]%ans])
				{
					visit[a[i]%ans]=1;
				}
				else
				{
					valid=false;
					break;
				}
			}
			if(valid)
			{
				cout<<ans<<"\n";
				break;
			}
			ans++;
		}	
	}  
	return 0;
}

Strange Way to Express Integers POJ2891

题意:求解线性同余方程组

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int maxn=1e6+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

int n;
ll a[maxn],r[maxn];

ll gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}
//y1a1+r1=y2a2+r2 --> a1y1-a2y2=r2-r1
ll solve(ll n,ll a[],ll r[])
{
	ll a1=a[1],r1=r[1];
	ll X,Y;
	for(int i=2;i<=n;++i)
	{
		ll A=a1,B=a[i],M=r[i]-r1;
		ll GCD=gcd(A,B,X,Y);
		if(M%GCD!=0)
			return -1;
		
		ll b1=B/GCD;
		X=(X*M/GCD%b1+b1)%b1;
		r1=a1*X+r1;
		a1=a1*b1;
	}
	return r1;
}

int main()
{
	while(~scanf("%d",&n))
	{
		rep(i,1,n)
			scanf("%d %d",&a[i],&r[i]);
		ll ans=solve(n,a,r);
		cout<<ans<<"\n";
	}
	return 0;
}

X问题 HDU1573

Problem Description
求在小于等于N的正整数中有多少个X满足: X m o d   a [ 0 ] = b [ 0 ] , X m o d   a [ 1 ] = b [ 1 ] , X m o d   a [ 2 ] = b [ 2 ] , … , X m o d   a [ i ] = b [ i ] , … ( 0 < a [ i ] < = 10 ) X mod\ a[0] = b[0], X mod\ a[1] = b[1], X mod\ a[2] = b[2], …, X mod\ a[i] = b[i], … (0 < a[i] <= 10) Xmod a[0]=b[0],Xmod a[1]=b[1],Xmod a[2]=b[2],,Xmod a[i]=b[i],(0<a[i]<=10)
Input
输入数据的第一行为一个正整数T,表示有T组测试数据。每组测试数据的第一行为两个正整数 N , M ( 0 < N < = 1000 , 000 , 000 , 0 < M < = 10 ) N,M (0 < N <= 1000,000,000 , 0 < M <= 10) NM(0<N<=1000,000,000,0<M<=10),表示X小于等于N,数组a和b中各有M个元素。接下来两行,每行各有M个正整数,分别为a和b中的元素
Output
对应每一组输入,在独立一行中输出一个正整数,表示满足条件的X的个数

思路:求解线性同余方程,对X最后求得的值,与N做一下大小讨论即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=15,INF=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;

ll a[maxn],b[maxn];

ll gcd(ll a,ll b,ll &x,ll&y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}

ll lcm(ll a,ll b)
{
	return a*b/gcd(a,b);
}

ll solve(ll n,ll a[],ll r[])
{
	ll a1=a[1],r1=r[1];
	for(int i=2;i<=n;++i)
	{
		ll A=a1,B=a[i],X,Y,M=r[i]-r1;
		ll GCD=gcd(A,B,X,Y);
		if(M%GCD!=0)
			return -1;
		ll b1=B/GCD;
		X=(X*M/GCD%b1+b1)%b1;
		r1=r1+a1*X;
		a1=a1*a[i]/GCD;
	}
	return r1;
}

int main()
{
	int T,N,m;
	cin>>T;
	while(T--)
	{
		cin>>N>>m;
		rep(i,1,m)
			cin>>a[i];
		rep(i,1,m)
			cin>>b[i];
		ll LCM=a[1];
		rep(i,2,m)
			LCM=lcm(LCM,a[i]);
		
		ll X=solve(m,a,b);
		ll ans=0;
		if(X==-1)
			ans=0;
		else if(X<=N&&X==0)
			ans=(N-X)/LCM;
		else if(X<=N&&X!=0)
			ans=(N-X)/LCM+1;
		cout<<ans<<"\n";
	}	
	return 0;
}

Hello Kiki HDU3579

Problem Description
One day I was shopping in the supermarket. There was a cashier counting coins seriously when a little kid running and singing “门前大桥下游过一群鸭,快来快来 数一数,二四六七八”. And then the cashier put the counted coins back morosely and count again…
Hello Kiki is such a lovely girl that she loves doing counting in a different way. For example, when she is counting X coins, she count them N times. Each time she divide the coins into several same sized groups and write down the group size Mi and the number of the remaining coins Ai on her note.
One day Kiki’s father found her note and he wanted to know how much coins Kiki was counting.
Input
The first line is T indicating the number of test cases.
Each case contains N on the first line, Mi ( 1 < = i < = N ) (1 <= i <= N) (1<=i<=N) on the second line, and corresponding A i ( 1 < = i < = N ) Ai(1 <= i <= N) Ai(1<=i<=N) on the third line.
All numbers in the input and output are integers.
1 < = T < = 100 , 1 < = N < = 6 , 1 < = M i < = 50 , 0 < = A i < M i 1 <= T <= 100, 1 <= N <= 6, 1 <= Mi <= 50, 0 <= Ai < Mi 1<=T<=100,1<=N<=6,1<=Mi<=50,0<=Ai<Mi
Output
For each case output the least positive integer X which Kiki was counting in the sample output format. If there is no solution then output -1.

思路:求解线性同余方程,当X解为0时,输出 l c m ( a 1 , a 2 , ⋯   , a n ) lcm(a_1,a_2,\cdots,a_n) lcm(a1,a2,,an)

#include <iostream>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define ll long long
using namespace std;

ll gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int q=gcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}

ll lcm(ll a,ll b)
{
	return a/gcd(a,b)*b;
}

ll solve(ll n,ll a[],ll r[])
{
	ll a1=a[1],r1=r[1];
	for(int i=2;i<=n;++i)
	{
		ll A=a1,B=a[i],X,Y,M=r[i]-r1;
		ll GCD=gcd(A,B,X,Y);
		if(M%GCD!=0)
			return -1;
		ll b1=B/GCD;
		X=(X*M/GCD%b1+b1)%b1;
		r1=r1+a1*X;
		a1=a1*a[i]/GCD;
	}
	return r1;
}

int main()
{
	ll T,n,a[10],r[10],Cas=0;
	cin>>T;
	while(T--)
	{
		cin>>n;
		rep(i,1,n)
			cin>>a[i];
		rep(i,1,n)
			cin>>r[i];
		ll LCM=1;
		rep(i,1,n)
			LCM=lcm(LCM,a[i]);
		
		ll ans=solve(n,a,r);
		if(ans==-1)
		{
			cout<<"Case "<<++Cas<<": -1\n";
			continue;
		}
		if(ans==0)
			ans=LCM;
		cout<<"Case "<<++Cas<<": "<<ans<<"\n";
	}	
	return 0;
}

Discrete Logging POJ2417

思路:BSGS模板题。先从 [ 1 , m − 1 ] [1,m-1] [1,m1]枚举j,把 ( A j % C , j ) (A^j\%C,j) (Aj%C,j)存到hash表中。然后从 [ 0 , m − 1 ] [0,m-1] [0,m1]枚举i,查找是否存在 B ⋅ A − i m % C B\cdot A^{-im}\%C BAim%C存在于hash表中,如果存在,那么 x = i m + j x=im+j x=im+j 就是答案

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

struct HashMap
{
	static const int MOD=76543;
	int key[MOD],value[MOD],head[MOD],next[MOD],id;
	
	void clear()
	{
		memset(head,-1,sizeof(head));
		id=0;
	}
	
	void insert(int x,int y)
	{
		int k=x%MOD;
		key[++id]=x;
		value[id]=y;
		next[id]=head[k];
		head[k]=id;
	}
	
	int find(int x)
	{
		int k=x%MOD;
		for(int i=head[k];i!=-1;i=next[i])
			if(key[i]==x)
				return value[i];
		return -1;
	}
}H;

int exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int q=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

int BSGS(int A,int B,int C)
{
	if(B==1)
		return 0;
	
	H.clear();
	A%=C;
	int m=ceil(sqrt(C*1.0));
	ll base=1;
	for(int j=0;j<=m-1;++j)
	{
		H.insert(base%C,j);
		base=(base*A)%C;
	}	
	
	ll a1=1,j;
	for(int i=0;i<=m-1;++i)
	{
		ll X,Y;
		exgcd(a1,C,X,Y);
		X=(X%C+C)%C;
		j=H.find(B*X%C);
		if(j!=-1)
			return i*m+j;
		a1=a1*base%C;
	}
	return -1;
}

int main()
{
	int A,B,C;
	while(~scanf("%d %d %d",&C,&A,&B))
	{
		int ans=BSGS(A,B,C);
		if(ans==-1)
			printf("no solution\n");
		else
			printf("%d\n",ans);
	}
	return 0;
}

Clever Y POJ3243

思路:扩展BSGS模板题

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

struct HashMap
{
	static const int MOD=76543;
	int key[MOD],value[MOD],head[MOD],next[MOD],id;
	
	void clear()
	{
		memset(head,-1,sizeof(head));
		id=0;
	}
	
	void insert(int x,int y)
	{
		int k=x%MOD;
		key[++id]=x;
		value[id]=y;
		next[id]=head[k];
		head[k]=id;
	}
	
	int find(int x)
	{
		int k=x%MOD;
		for(int i=head[k];i!=-1;i=next[i])
			if(key[i]==x)
				return value[i];
		return -1;
	}
}H;

int exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int q=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return q;
}

ll elimination(ll &A,ll &B,ll &C)
{
	ll GCD,X,Y,A0=1,k=0;
	while((GCD=exgcd(A,C,X,Y))!=1)
	{
		if(B%GCD!=0)
			return -1;
		B/=GCD;
		C/=GCD;
		A0=A0*A/GCD%C;
		k++;
	}
	if(k==0)
		return 0;
	exgcd(A0,C,X,Y);
	B=(X*B%C+C)%C;
	return k;
}

ll EXBSGS(ll A,ll B,ll C)
{
	H.clear();
	A%=C;
	
	ll a1=1;
	for(int i=0;i<=100;++i)
	{
		if((a1-B)%C==0)
			return i;
		a1=(a1*A)%C;
	} 
	
	ll k=elimination(A,B,C);
	if(k==-1)
		return -1;
	
	int m=ceil(sqrt(C*1.0));
	ll base=1;
	for(int j=0;j<=m-1;++j)
	{
		H.insert(base%C,j);
		base=base*A%C;
	}
	
	ll j;
	a1=1;
	for(int i=0;i<=m-1;++i)
	{
		ll X,Y;
		exgcd(a1,C,X,Y);
		X=(X%C+C)%C;
		j=H.find(B*X%C);
		if(j!=-1)
			return i*m+j+k;
		a1=(a1*base)%C;
	}
	return -1;
}

int main()
{
	ll A,B,C;
	while(scanf("%lld %lld %lld",&A,&C,&B)&&(A||B||C))
	{
		ll ans=EXBSGS(A,B,C);
		if(ans==-1)
			printf("No Solution\n");
		else
			printf("%lld\n",ans);
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值