AtCoder Beginner Contest 335 (Sponsored by Mynavi) G. Discrete Logarithm Problems(群论的阶 拉格朗日定理)

题目

AtCoder Beginner Contest 335 (Sponsored by Mynavi) G. Discrete Logarithm Problems(群论的阶 拉格朗日定理)_第1张图片

n(n<=2e5)个数,第i个数ai(ai

求存在正整数k,使得a_{i}^k\equiv a_{j}(mod\ p)成立的(i,j)(1<=i,j<=n)对数

思路来源

官方题解

heltion代码

题解

赛中搞了原根+bsgs一大堆,后来发现根本没必要

结论

称满足a^x\equiv 1(mod\ p),gcd(a,p)=1的最小正整数x为a的阶,记作ord_p(a)

ord_p(b)|ord_p(a),则方程a^k\equiv b(mod\ p)有解

拉格朗日定理

gcd(a,n)=1,有ord_n(a)|\varphi (n)

感性证明

4^6\equiv 3^3 \equiv 1(mod\ 13)\Leftrightarrow 4^2 \equiv 3 (mod\ 13)

感觉无需深究,当原根/群论的一个性质记就可以

方法论

试除法,找到每个ai的阶,

即如果除掉一个素因子x后还成立,就除掉一个素因子

得到每个数的阶后,1e13以内的数的约数最大1e4,但实际没有那么多

还有阶为x的恰有phi(x)个这样的限制

暴力n^2遍历约数即可,这里带了个map的log也过了

代码

#include
using namespace std;
// #include
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
#define LL long long
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<fac;
mapnow;
ll modpow(ll x,ll n,ll mod){
    ll res=1;
    for(;n;n>>=1,x=(LL)x*x%mod){
        if(n&1)res=(LL)res*x%mod;
    }
    return res;
}
int main(){
    scanf("%d%lld",&n,&p);
    rep(i,1,n){
        scanf("%lld",&a[i]);
    }
    d=p-1;
    for(ll i=2;i*i<=d;++i){
        if(d%i==0){
            fac.pb(i);
            while(d%i==0)d/=i;
        }
    }
    if(d>1)fac.pb(d);
    rep(i,1,n){
        ll d=p-1;
        for(auto &x:fac){
            while(d%x==0 && modpow(a[i],d/x,p)==1){
                d/=x;
            }
        }
        now[d]++;
        //printf("i:%d d:%lld\n",i,d);
    }
    ll ans=0;
    for(auto &x:now){
        for(auto &y:now){
            if(x.fi%y.fi==0){
                ans+=1ll*x.se*y.se;
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(数论,群论,阶,拉格朗日定理)