同余方程与逆元的各种操作,牛客序列操作

本文探讨了在处理同余方程序列操作时如何利用逆元优化算法,避免超时。当质数p为模数时,通过快速幂运算求逆元,实现O(p)时间复杂度解题。若p非质数,可通过欧拉筛预处理欧拉函数phi[i],调整逆元计算方式。此外,还提供了扩展欧几里得算法用于求逆元的代码示例。
摘要由CSDN通过智能技术生成

1.

显然操作1,n是最优

对于这道题,其实就是对ai+kx=b[i],其中x是我们要确定的数,那就相当于同余方程kx≡b[i]-a[i](mod p),那么b[i]-a[i]我们可以映射为0到p-1之间,因为其他值我们肯定也可以通过加n*p得到(这里bi-a[i]提前modp之后的值忽略)。那就是枚举x(要的答案值),对每一个x再枚举(b[i]-a[i])%p的值,所以时间复杂度为p*p*logp(logp是扩展欧几里得所需要的时间复杂度),这样我们就超时了。

        那这个时候逆元就出来了,首先逆元在ax≡1(mod p)中,如果要化为a≡1*(x-1)(mod p)这里是x的负一次,即/ x,那么就要求p是质数,那这道题刚好保证p是质数,那我们直接带即可,那最终答案要求的k即为这里的a,就可以直接选预处理逆元,然后直接求解,去掉log的时间复杂度。题目具体解法可参考下面这个:

题解 | #序列操作#_牛客博客

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+10;
int a[N],b[N],c[N];
LL fast(LL a,LL b,LL p){
	ll res=1%p;
	while(b){
		if(b&1)res=res*a%p;
		b=b/2;
		a=a*a%p; 
	}
	return res;
}
int exgcd(int a,int b,int &x,int &y){
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
    //正常求最大公约数的同时更新系数
}
int main() {
	int n,p;
	cin>>n>>p;
	for(int i=1;i<=n;i++)cin>>a[i],a[i]%=p;
	for(int i=1;i<=n;i++)cin>>b[i];
	for(int i=1;i<=n;i++){
		int t=(b[i]-a[i]+p)%p;
		c[t]++;
	}
    int flag = 1;
    for (int i = 1; flag && i <= n; ++i) if(a[i] != b[i]) flag = 0;
    if(flag)
    {
        puts("0");
        return 0;
    }
	int ans=0x3f3f3f3f,nx;
	for(int k=1;k<p;k++){
		int inv=fast(k,p-2,p);
		int t=0;
		for(int j=1;j<p;j++)
		{
			if(!c[j])continue;
			t=max(inv*j%p,t);
//			int x,y;
//			int d=exgcd(k,p,x,y);
//			LL g=p/d;
//			x=((x*j/d)%g+g)%g;//p*p*logp的写法
		}
		if(t<ans){
			ans=t,nx=k;
		}
	}
	cout<<nx<<'\n';
	return 0;
}

2.

如果当p不是质数时,我们仍要求逆元,我们可以通过欧拉筛求出所有数的phi[i]值,那我们就可以不管是否p为质数,直接可以求,此时逆元inv=fast(a,phi[a]-1,p);相当于求逆元时p-2改为phi[a]-1;

3.

我们上面写法得预处理出欧拉函数phi的值,那如果phi[i]中i过大时,我们是不太好求出他的逆元的,那此时就可以用扩展欧几里得的写法,这里直接给出代码

LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	LL ans=ex_gcd(b,a%b,y,x);
	y-=a/b*x;
	return ans;
}
 
LL inverse(LL a,LL n)//直接带要求的数a,和模数n
{
    LL d,x,y;
    d=ex_gcd(a,n,x,y);
    return d==1?(x+n)%n:-1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

first vegetable

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值