|
|
更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19648636
大意 区间加,求 $a[l]^{a[l+1]^{a[l+2]^{\dots ^{a[r]}}}} \mod p$ 的值。
思路 对于这个指数塔,非常逆天,我们首先想到的肯定的是与降幂有关的内容,也就只有欧拉定理了,由于 $p$ 并非是质数,我们需要用扩展欧拉定理,那么什么是欧拉定理和扩展欧拉呢? $\varphi(n)$ 就表示小于等于 $n$ 与 $n$ 互质的数的个数,$\varphi(1) = 1, \varphi(p) = p - 1$,这里的 $p$ 是质数。那么 $\phi(n)$ 应该怎么算呢?我们由素数分解定理,就可以有这样的公式: $$n \prod_{p | n} (1 - \frac{1}{p})$$ 这个过程我们是可以用线性筛预处理这个 $\varphi(n)$ 的。 好的,预处理讲完了,接下来讲欧拉定理,当 $\gcd(a, m) = 1$,那么 $a ^ {\varphi(m)} \equiv 1 \pmod m$,这个在模数为质数的时候就可以快速降幂,但是要是不是质数呢?没事,我们有扩展欧拉定理,其内容如下: $$a ^ b \equiv \begin{cases} a ^ {b \pmod{\varphi(m)}} & \gcd(a, m) = 1 \\ a ^ b & \gcd(a, m) \neq 1, b < \varphi(m) \\ a ^ {b \pmod{\varphi(m)} + \varphi(m)} & \gcd(a, m) \neq 1, b \ge \varphi(m) \end{cases} \pmod{m}$$ 好了,知道了这些,我们回到这个题目,对于这个指数塔,其指数一直取 $\varphi$ 显然最后会变成 $1$,那么要取多少次呢?我们想想对于 $\varphi(2 ^ k)$ 来说,其等于 $2 ^ {k - 1}$,对于质数是 $p - 1$,其如果是质数就减去一变成偶数,然后就会很快变小。 那么我们这个题直接写递归的话,递归层数最多就是 $\log$ 的层级,我们为了维护区间加和单点查询,我们直接用个树状数组维护即可,在递归的过程中,不断判断模数是否为 $1$,注意快速幂的地方,也要改一下。
代码
#include<iostream>
using namespace std;
#define int long long
const int MAXN = 2 * 1e7 + 5;
int a[MAXN], n, m;
int pri[MAXN], phi[MAXN], tot = 0;
int sum[MAXN];
bool vis[MAXN];
void init(){
phi[1] = 1;
for(int i = 2;i < MAXN;i ++){
if(!vis[i]){
phi[i] = i - 1;
pri[++ tot] = i;
}
for(int j = 1;j <= tot && i * pri[j] < MAXN;j ++){
vis[i * pri[j]] = 1;
if(i % pri[j] == 0){
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
// for(int i = 1;i < 5;i ++){
// cout << phi[i] << ' ';
// }
}
int lowbit(int x){
return x & -x;
}
void add(int x, int k){
while(x <= n){
sum[x] += k;
x += lowbit(x);
}
}
int ask(int x){
int res = 0;
while(x){
res += sum[x];
x -= lowbit(x);
}
return res;
}
int qpow(int a, int b, int p){
int res = 1;
bool flag = 0;
if(a >= p){
flag = 1;
a %= p;
}
while(b){
if(b & 1) res = res * a;
if(res >= p){
res %= p;
flag = 1;
}
a = a * a;
if(a >= p){
a %= p;
flag = 1;
}
b >>= 1;
}
return flag ? res + p : res;
}
int solve(int l, int r, int p){
int val = a[l] + ask(l);
if(p == 1) return 1;
if(l == r){
if(val >= p) return val % p + p;
else return val;
}
int cnt = solve(l + 1, r, phi[p]);
return qpow(val, cnt, p);
}
signed main(){
cin.tie(0) -> ios::sync_with_stdio(0);
init();
cin >> n >> m;
for(int i = 1;i <= n;i ++){
cin >> a[i];
}
for(int i = 1;i <= m;i ++){
int op, l, r, x;
cin >> op >> l >> r >> x;
if(op == 1) add(l, x), add(r + 1, -x);
else cout << solve(l, r, x) % x << '\n';
}
return 0;
}
题目4228 [Ynoi Easy Round 2016] Nephren Ruq Insania
4
评论
2026-02-28 13:31:34
|