一道比较裸的扩欧模板题

扩展欧拉定理

ac={ac modϕ(p),gcd(a,p)=1ac,gcd(a,p)1,c<ϕ(p)ac mod ϕ(p)+ϕ(p),gcd(a,p)=1,cϕ(p)a^c=\begin{cases}a^{c~mod`\phi(p)},gcd(a,p)=1\\a^c,gcd(a,p) \ne1,c < \phi(p)\\a^{c~mod~\phi(p)+\phi(p)},gcd(a,p)=1,c\ge \phi(p)\end{cases}

具体证明就不再这里讲解了主要是不会

很明显,可以直接用欧拉定理递归降幂,那么怎么保证时间复杂度是对的呢

我们将m分解为 i=1kpiqi\prod\limits_{i=1}^k p_i^{q_i}

那么 ϕ(m)=ϕ(i=1kpiqi)=i=1k(pi1)piqi1\phi(m)=\phi(\prod\limits_{i=1}^k p_i^{q_i})=\prod\limits_{i=1}^k (p_i-1)p_i^{q_i-1}

到这里就可以看出,每迭代一遍欧拉函数,就相当于把 mm 中的所有质因子全部取出来减个一再乘回去,要得到 ϕ(m)=1\phi(m)=1,只需要迭代 mm 中每个质因子可以取出来的2的数量,时间复杂度是 loglog 级的

对于题目中的每一个 ϕ\phi 可以先预处理一遍,用 mapmap 存起来

代码

#include<iostream>
#include<cstdio>
#include<unordered_map>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;

unordered_map<ll, ll> phi;
ll w[N];

ll pi(ll p)
{
ll res = p;
for (ll i = 2; i * i <= p; i++)
{
if (p % i == 0)
res -= res / i;
while (p % i == 0)
p /= i;
}
if (p > 1)
res -= res / p;
return res;
}

ll qmi(ll a, ll b, ll c)
{
ll res = 1;
bool flag = false;
while (b)
{
if (b & 1)
{
res *= a;
if (res >= c)
flag = true;
res %= c;
}
a *= a;
if (a >= c)
flag = true;
a %= c;
b >>= 1;
}
if (flag)
res += c;//需要满足欧拉定理
return res;
}

ll dfs(ll l, ll r, ll p)
{
if (l == r + 1 || p == 1)
return 1;
return qmi(w[l], dfs(l + 1, r, phi[p]), p);
}

int main()
{
ll n, p;
scanf("%lld%lld", &n, &p);
for (ll i = 1; i <= n; i++)
scanf("%lld", &w[i]);
ll now = p;
while (now != 1)
phi[now] = pi(now), now = phi[now];
phi[now] = 1;
ll q;
scanf("%lld", &q);
while (q--)
{
ll l, r;
scanf("%lld%lld", &l, &r);
printf("%lld\n", dfs(l, r, p) % p);
}
return 0;
}