数论——拓展欧几里德算法复习

最近也是在备战比赛,所以也是来小小的复习了一下以前学的东西

最重要的是第一道题!

最重要的是第一道题! 

最重要的是第一道题!

先放拓欧板子(不懂怎么推出了就发在评论区或者私聊)

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

例题:

P2054 [AHOI2005] 洗牌

 我们先来通过6张牌来列举一下

初始        [  1 , 2 , 3 , 4 , 5 , 6  ]

第一次    [  4 , 1 , 5 , 2 , 6 , 3  ]

第二次    [  2 , 4 , 6 , 1 , 3 , 5  ]

第三次   [  1 , 2 , 3 , 4 , 5 , 6  ]

我们通过上面对1这个数据分析,

初始值在1位置,第一次变换后在2位置,第二次变换后在4位置,第三次变换后在1位置

我们发现每次都相当于乘2,但是为什么会被突然折断呢?我们不难想到可能是存在取模操作

因此我们可以大胆猜想这个数是7,也就是(n+1)

我们再对3这个数据进行分析

初始值在3位置,第一次在6位置,第二次再5位置,第三次在3位置

我们发现也是每次位置都乘2,然后取模7(n+1)

因此我们就发现了规律

第i个数m次操作后的位置为 :( i* 2^m)%(n+1)

但是题意问的是m次操作之后,谁在L这个位置,不是问你每个数在哪,而是问你在特定位置的数,很明显,我们一开始想的是遍历一遍所有数,找到谁在那个位置,但是数据是10^10,很明显会超时,因此我们只能去倒推一遍 这个式子

假设x在L那个位置,那么

(2^m)*x= (n+1) *k + L 

请问你看这个式子想到了什么???

我嘞个豆,啥也没想到,你是要毁了我吗?孩子

那我再给你将这个式子变一下形式

(2^m)*x + (n +1) *y =  L  这下能看出来拓展欧几里得的形式了吧

为什么能用拓欧呢?因为题目上说了,n是一个偶数,n+1一定是个奇数,2^m次方一定是个偶数,所以肯定互质的

但是这个式子也很麻烦,能不能再变一下呢?那肯定是可以的

我们先去求 

(2^m)*x + (n +1) *y =  1,然后最后给x乘以L不就是了,说罢,我的气息终于不再掩饰,顷刻将代码炼化出来

77pts

#include <bits/stdc++.h>  
using namespace std;  
#define int long long  

int n, m, l;  
int mod;  

int fast(int a, int b, int mod) 
{  
    int result = 1;  
    int base = a % mod;   
    while (b > 0) 
	{  
        if (b % 2 == 1) 
		{  
            result = (result * base) % mod;  
        }  
        base = (base * base) % mod;  
        b /= 2;  
    }  
    return result;  
}  

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

signed main() {  
    ios_base::sync_with_stdio(0);  
    cin.tie(0);  
    cout.tie(0);  

    cin >> n >> m >> l;  
    mod = n + 1;   
    int x, y;  
    int f = fast(2, m, mod);
    int z = exgcd(f, mod, x, y); 
    x = (x+mod) % mod; 
    cout << (x*l) % mod << '\n'; 
} 

 没想到被暗算了,在运算过程中有可能会导致超出long long的范围,因此需要用到龟速幂,也就是在乘法的过程中用快速乘去运算 

84pts

#include <bits/stdc++.h>  
using namespace std;  
#define int long long  

int n, m, l;  
int mod;  


int mul(int a,int b)
{
	int result=0;
	while(b>0)
	{
		if(b%2==1)
		{
			result=(result+a)%mod;
		}
		a=(a+a)%mod;
		b/=2;
	}
	return result%mod;
}

int fast(int a, int b) 
{  
    int result = 1;  
    int base = a % mod;   
    while (b > 0) 
	{  
        if (b % 2 == 1) 
		{  
            result=mul(result,base);
        }  
        base = mul(base,base);  
        b /= 2;  
    }  
    return result%mod; 
}  


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

signed main() {  
    ios_base::sync_with_stdio(0);  
    cin.tie(0);  
    cout.tie(0);  

    cin >> n >> m >> l;  
    mod = n + 1;   
    int x, y;  
    int f = fast(2, m);
    int z = exgcd(f, mod, x, y); 
    x = (x+mod) % mod; 
    cout << (x*l) % mod << '\n'; 
} 

为什么还是错一个点嘞?左思右想都没想明白,

才发现,在逆元前面还是有可能会爆数据,为什么不能用乘法逆元的积性函数的性质,我先去求2和n+1的逆元然后再去m次方后再去乘以L,思考便后,我便不再掩饰自己的气息,仿佛有成尊之势

果然AC了

100pts

#include <bits/stdc++.h>  
using namespace std;  
#define int long long  

int n, m, l;  
int mod;  

int mul(int a,int b)
{
	int result=0;
	while(b>0)
	{
		if(b%2==1)
		{
			result=(result+a)%mod;
		}
		a=(a+a)%mod;
		b/=2;
	}
	return result%mod;
}

int fast(int a, int b) 
{  
    int result = 1;  
    int base = a % mod;   
    while (b > 0) 
	{  
        if (b % 2 == 1) 
		{  
            result=mul(result,base);
        }  
        base = mul(base,base);  
        b /= 2;  
    }  
    return result%mod; 
}  


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

signed main() 
{  
    ios_base::sync_with_stdio(0);  
    cin.tie(0);  
    cout.tie(0);  
    
    cin >> n >> m >> l;  
    mod = n + 1;   
    int x, y;  
    exgcd(2, mod, x, y); 
    x = (x+mod) % mod; 
    x=fast(x,m);
    cout<<mul(x,l) << '\n'; 
} 

P5656 【模板】二元一次不定方程 (exgcd)

思路:题目不是很难,但是处理的条件颇多,只要有耐心并且细心一点都可以写出来

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int a,b,c;
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int x2,y2;
	int d=exgcd(b,a%b,x2,y2);
	x=y2;
	y=x2-a/b*y2;
	return d;
}
signed main()
{
	cin>>t;
	while(t--)
	{
		int x,y;
		int d;
		int minnx,minny,maxnx,maxny;
		int cnt=0;
		cin>>a>>b>>c;
		d=gcd(a,b);
		if(c%d!=0)
		{
			cout<<-1<<"\n";
		}
		else
		{
			a/=d,b/=d,c/=d;
			exgcd(a,b,x,y);
			x*=c,y*=c;
			if(x>0&&x%b!=0)
			{
				minnx=x%b;
			}
			else
			{
				minnx=x%b+b;
			}
			maxny=(c-minnx*a)/b;
			if(y>0&&y%a!=0)
			{
				minny=y%a;
			}
			else
			{
				minny=y%a+a;
			}
			maxnx=(c-minny*b)/a; 
			if(maxnx>0)
			{
				cnt=(maxnx-minnx)/b+1;
			}
			if(cnt==0)
			{
				cout<<minnx<<" "<<minny<<"\n"; 
			}
			else
			{
				cout<<cnt<<" "<<minnx<<" "<<minny<<" "<<maxnx<<" "<<maxny<<"\n";
			}
		}
	}
	return 0;
}

 P1516 青蛙的约会

思路:这题我们可以写出一个方程

假设第一只青蛙的起始位置为n,速度为a,第二只青蛙的起始位置为m,速度为b 

可以写出方程

(a-b)*t+L *y=m-n

想到了什么?当然是拓欧

直接去写拓欧去求出最小的x,然后x*(m-n)/gcd(a-b,L)即可,当然不要忘记取模L

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a,b,l;
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}

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

signed main()
{
	int x,y;
	cin>>n>>m>>a>>b>>l;
	if(a<b)
	{
		swap(a,b),swap(n,m);
	}
	a=a-b;
	b=l;
	int d=gcd(a,b);
	int c=(m-n+l)%l;
	exgcd(a,b,x,y);
	if(c%d!=0)
	{
		cout<<"Impossible\n";
		return 0;
	}
	l/=d;
	cout<<(c/d*x%l+l)%l;
	return 0;
}

P3951 [NOIP2017 提高组] 小凯的疑惑

思路:其实就是看自己的思维的,如果想要能够被表示那么就说明x和y都要大于0,所以不能表示的最小数一定为

-a+?*b(?表示现在还不知道)

那为什么一定为这种形式呢?

x不能等于-2吗?

肯定可以啊,只不过一定不是最大,因为还有一个x=-1的时候更大

那么?的范围是什么,最大是(a-1) 

为什么呢?假如可以为a的话,那么方程就可以化简为a*(b-1),那么就可以用a类型钞票单独表示了

最后就可以写出-a+b*(a-1)

也就是a*b-a-b;

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
	int a,b;
	cin>>a>>b;
	cout<<(a*b-a-b);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值