Gravatar
终焉折枝
积分:1684
提交:221 / 391

Pro4228  [Ynoi Easy Round 2016] Nephren Ruq Insania

更好的阅读体验: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;
}



2026-02-28 13:31:34    
我有话要说
暂无人分享评论!