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;
}