知识点-模方程合集

知识点-模方程合集

解决问题类型:

1.线性同余方程

a ⋅ x = b ( m o d n ) a \cdot x = b \pmod n ax=b(modn)

2.线性同余方程组

{ x ≡ a 1 ( m o d   m 1 ) x ≡ a 2 ( m o d   m 2 ) … x ≡ a n ( m o d   m n ) \begin{cases} x\equiv a_1 (mod\ m_1)\\ x\equiv a_2 (mod\ m_2)\\ \dots\\ x\equiv a_n (mod\ m_n) \end{cases} xa1(mod m1)xa2(mod m2)xan(mod mn)

3.模意义下取对数

a x ≡ b ( m o d m ) a^x \equiv b \pmod m axb(modm)

4.高次同余方程/模意义下开根

x k ≡ a ( m o d n ) x^k \equiv a \pmod n xka(modn)

细节证明点这里:https://www.cnblogs.com/SuuT/p/10572283.html

模板即复杂度:

1.线性同余方程

O ( l o g n ) O(logn) O(logn)

法1.n,a互质时使用逆元,否则两边除掉gcd(a,x)然后逆元。(如果b除不尽及无解)
法2.化为 a ⋅ x + n ⋅ k = b a \cdot x + n \cdot k = b ax+nk=b 扩展欧几里得

2.线性同余方程组

O ( n 2 ) O(n^2) O(n2)
x = k ∏ i = 1 n m i + ∑ i = 1 n a i M i M i − 1 x=k\prod_{i=1}^{n}m_i+\sum_{i=1}^{n}a_iM_iM_i^{-1} x=ki=1nmi+i=1naiMiMi1

ll Sunzi(ll *m,ll *a,int len){
    ll lcm = 1;
    ll ans = 0;
    for (int i=0;i<len;i++){
        ll k0,ki;
        ll d = ext_gcd(lcm,m[i],k0,ki);
        if ((a[i]-ans)%d!=0) return -1;
        else {
            ll t = m[i]/d;
            k0 = ( k0*(a[i]-ans)/d%t + t)%t;
            ans = k0*lcm + ans;
            lcm = lcm/d*m[i];
        }
    }
    return ans;
}
3.模意义下取对数 BGSG

O ( p ) O(\sqrt{p}) O(p ).

考虑求解方程
a x ≡ b   ( m o d   p ) a^x\equiv b\ (mod\ p) axb (mod p)
这样的 x x x 称为离散对数,可以写为 log ⁡ a b   ( m o d   p ) \log_a b\ (mod\ p) logab (mod p)

Baby step giant step 算法

x = k n + i x=kn+i x=kn+i n n n 为某常正整数),则原方程可以写成 ( a n ) k = b ( a − 1 ) i (a^n)^k=b(a^{-1})^i (an)k=b(a1)i

( b ( a − 1 ) i , i ) , i = 0 , 1 , … , n − 1 \left(b\left(a^{-1}\right)^i,i\right),i=0,1,\dots,n-1 (b(a1)i,i),i=0,1,,n1 存入表 (table,C++中可以用unordered_map) 中,然后枚举 k k k ,在表中查找 ( a n ) k (a^n)^k (an)k 即可,复杂度为 O ( n + p n ) O(n+\frac{p}{n}) O(n+np) ,取 n = p n=\sqrt{p} n=p ,那么复杂度为 O ( p ) O(\sqrt{p}) O(p )

ll bsgs(ll a,ll b,ll p){
    static unordered_map<ll,ll> tab;
    tab.clear();
    ll u=(ll)sqrt(p)+1;
    ll now=1,step;
    rep(i,0,u-1){
        ll tmp=b*inv(now,p)%p;
        if(!tab.count(tmp))tab[tmp]=i;
        (now*=a)%=p;
    }
    step=now;
    now=1;
    for(ll i=0;i<p;i+=u){
        if(tab.count(now))return i+tab[now];
        (now*=step)%=p;
    }
    return -1;
}
4.高次同余方程/模意义下开根

O ( p ) O(\sqrt{p}) O(p ).

考虑求解方程
x a ≡ b   ( m o d   p ) x^a\equiv b\ (mod\ p) xab (mod p)
先求 p p p 的原根 g g g ,设 x ≡ g u   ( m o d   p ) x\equiv g^u\ (mod\ p) xgu (mod p) b ≡ g t   ( m o d   p ) b\equiv g^t\ (mod\ p) bgt (mod p) ,用BSGS算法求出 t t t ,方程可写成
g a u ≡ g t   ( m o d   p ) g^{au}\equiv g^t\ (mod\ p) gaugt (mod p)
进而有
a u + ( p − 1 ) v = t au+(p-1)v=t au+(p1)v=t
用扩展欧几里得算法求出 u u u ,也就求出了 x x x

//primitiveRoot 函数在《知识点-原根》中
ll ModEquationSolve(ll a,ll y,ll p){
    a%=p-1;
    ll g=primitiveRoot(p),t=bsgs(g,y,p),z,z_,d=ext_gcd(a,p-1,z,z_);
    if(t%d!=0)return -1;
    ll tmp=(p-1)/d;
    z=(t/d*z%tmp+tmp)%tmp;
    return fpow(g,z,p);
}
附:原根定义

定理6.2.1 ( Z p ∗ , ⋅ ) (\mathbb{Z}_p^*,\cdot) (Zp,) 是循环群,即存在 a ∈ Z p ∗ a\in \mathbb{Z}_p^* aZp ,使得
Z p ∗ = { a n ∣ n = 1 , 2 , … , p − 1 } \mathbb{Z}_p^*=\{a^n|n=1,2,\dots,p-1\} Zp={ann=1,2,,p1}
这样的 a a a 称为 p p p 的原根

例题1

Shuffling反复洗牌

代码1

题解

//HDU3430 (置换群循环节+中国剩余定理)
//https://www.cnblogs.com/hnqw1214/p/6602341.html
int main()
{
    while (~scanf("%d",&n))
    {
        if (n==0) break;
        int i;
        for (i=1;i<=n;i++) scanf("%d",&a[i]);
        for (i=1;i<=n;i++) scanf("%d",&b[i]);
        memset(vis,0,sizeof(vis));
        int cnt=0;
        bool can=true;
        for (i=1;i<=n;i++) if (!vis[i])
        {
            int flag=i;
            int count=0;
            while (!vis[flag])
            {
                vis[flag]=1;
                c[count++]=flag;
                flag=a[flag];
            }
            int pos=0;
            while (pos<count&&b[i]!=c[pos]) pos++;
            if (pos==count)
            {
                can=false;
                break;
            }
            x[cnt]=count;
            y[cnt++]=pos;
            //cout<<count<<" "<<pos<<endl;
            flag=a[i];
            while (flag!=i)
            {
                if (b[flag]!=c[(++pos)%count])
                {
                    can=false;
                    break;
                }
                flag=a[flag];
            }
            if (!can) break;
        }
        if (!can) puts("-1");
        else printf("%lld\n",china_remain(cnt,x,y));
    }
}

例题2

Discrete Logging板题

底数和模数不互质版

代码2

//https://www.cnblogs.com/Konjakmoyu/p/5180458.html
#include<cstdio>
#include<cstring>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define Maxn 40000
const int pp=(1<<16)-1;

LL x,z,k,aa,m;
int cnt,num;
int ok;

struct node
{
    int idx,nt;
    LL val;
}baby[2*Maxn];int len;

LL ax,ay;
LL exgcd(LL a,LL b)
{
    if(b==0) {ax=1,ay=0;return a;}
    LL g=exgcd(b,a%b);
    LL yy=ay;
    ay=ax-a/b*ay;ax=yy;
    return g;
}

void ins(LL now,int id)
{
    int x=now&pp;
    if(baby[x].idx==-1) {baby[x].idx=id;baby[x].val=now;return;}
    while(baby[x].val!=now&&baby[x].nt!=-1) x=baby[x].nt;
    if(baby[x].val==now) return;
    baby[x].nt=++len;
    baby[len].nt=-1;baby[len].val=now;baby[len].idx=id;
}

bool init()
{
    scanf("%lld%lld%lld",&x,&z,&k);
    
    if(x==0&&z==0&&k==0) return 0;k%=z;
    LL g,bm;
    bm=1%z;aa=1,num=0;ok=1;
    
    for(int i=0;i<=100;i++) if(bm==k) {printf("%d\n",i);ok=0;return 1;}
    else bm=(bm*x)%z;
    while((g=exgcd(x,z))!=1)
    {
        z/=g,aa=(aa*x/g)%z;num++;
        if(k%g!=0) {ok=-1;break;}
        k/=g;
    }
    
    return 1;
}

int ffind(LL now)
{
    int x=now&pp;
    if(baby[x].idx==-1) return -1;
    while(baby[x].val!=now&&baby[x].nt!=-1) x=baby[x].nt;
    if(baby[x].val!=now) return -1;
    return baby[x].idx;
}

LL BSGS()
{
    m=(LL)(ceil(double(sqrt((double)z))));
    
    
    for(int i=0;i<=pp;i++) baby[i].idx=baby[i].nt=-1;
    
    LL now=aa%z; len=pp;
    for(int i=0;i<=m;i++) {ins(now,i);now=(now*x)%z;}
    
    LL bm=1%z,ans=-1;
    for(int i=1;i<=m;i++) bm=(bm*x)%z;
    LL g=exgcd(bm,z);
    bm=ax/g; bm=(bm%(z/g)+(z/g))%(z/g);
    if(bm==0) bm=z/g;
    
    LL tmp=k;
    for(int i=0;i<=m;i++)
    {
        int j;
        if((j=ffind(tmp))!=-1)
        {
            ans=i*m+j;
            break;
        }
        tmp=(tmp*bm)%z;
    }
    return ans;
}

int main()
{
    while(1)
    {
        LL ans;
        if(!init()) break;
        if(ok==0) continue;
        else if(ok==-1) printf("No Solution\n");
        else
        {
            ans=BSGS();
            if(ans==-1) printf("No Solution\n");
            else printf("%lld\n",ans+num);
        }
    }
    return 0;
}

poj3243 (hash版)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Best KeyBoard

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

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

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

打赏作者

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

抵扣说明:

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

余额充值