线性求逆元

若a*x≡1(mod b) a,b互质,则称x为a的逆元,记为a-1。在计算(t/a)mod b 时等价于t*a-1mod b。

显然可转化为ax+by=1,用扩欧来求解,但相对来说是比较慢的,这里还有线性算法:

首先1-1≡1(mod p)

设p=k*i+r,那么k*i+r≡0(mod p)两边同乘i-1,r-1,可得在mod p 意义下:

k*r-1+i-1≡0

→i-1≡-k*r-1

→i-1≡-(p/i)*(p mod i)-1

于是我们就求得了递推式:a[i]=-(p/i)*a[p%i](a[1]=1,因为p%i<i所以a[p%i]会在之前求出)

就可线性求解乘法逆元:

1.板子

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,p,a[3000010];
int main()
{
	scanf("%d%d",&n,&p);
	a[1]=1;
	for(int i=2;i<=n;i++)
	a[i]=-(long long)(p/i)*a[p%i]%p;
	for(int i=1;i<=n;i++)
	{
	if(a[i]<0)a[i]+=p;
	printf("%d\n",a[i]);	
	}
}

 例题:

[AHOI2005]洗牌

 

 

 

我们先按样例找下规律:

1,4,2,1

2,1,4,2

3,5,6,3

4,2,1,4

5,6,3,5

6,3,5,6

//每一列代表一次洗牌。

我们发现每次的位置变化为当前位置*2再取余(n+1),

设当前位置为x

那么下一次位置为2*x%(n+1)

下m次位置为2m*x%(n+1)

我们要求的就是满足2m*x≡l (mod n+1)的x.

那么x≡l*(2m)-1(mod n+1)

所以只要求出2m在模n+1意义下的逆元就可以求出x

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define mod (n+1)
using namespace std;
int n,l,m,x,y;
int _pow(int x,int y)
{
    int ans=1;
    for(;y;y>>=1)
    {
        if(y&1)
        ans=(long long)ans*x%(n+1);
        x=(long long)x*x%(n+1);
    }
    return ans%(n+1);
}
void exgcd(int a,int b)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b);
    x^=y^=x^=y;
    y-=a/b*x;
}
int main()
{
    scanf("%d%d%d",&n,&m,&l);
    int a=_pow(2,m);
    exgcd(a,n+1);
    while(x<=0)x+=n+1;
    if((long long)x*l%(n+1)==0)
    {
        cout<<n+1;
        return 0;
    }
    printf("%lld",(long long)x*l%(n+1));
}

 

posted @ 2018-04-15 09:27  _ZZH  阅读(297)  评论(0编辑  收藏  举报