[Ynoi Easy Round 2016] 炸脖龙 I

[Ynoi Easy Round 2016] 炸脖龙 I

一道简单的 Ynoi 题。

题意

给定长度为 \(n\) 的序列 \(a\),有 \(m\) 次操作,每次操作有两种形式:

  • 区间 \([l,r]\)\(x\)

  • \(a[l]^{a[l+1]^{a[l+2]^{\dots^{a[r]}}}} \bmod p\)

\(1 \leq n,m \leq 5 \times 10^5\)\(1 \leq p \leq 2 \times 10^7\)

思路

首先我们需要知道扩展欧拉定理:

\[a^b \equiv \begin{cases} a^b && b < \varphi(m)\\ a^{b \bmod \varphi(m) + \varphi(m)} && b \geq \varphi(m) \end{cases} (\bmod \space m) \]

我们发现,对于 \(x\) 不停求 \(\varphi(x)\),最多经过 \(O(\log x)\) 次后变为 \(1\)。所以对于查询操作,实际只有 \(O(\log p)\) 个元素是有用的,暴力查询即可。这也是 Power Tower

对于区间修改,我们可以使用树状数组维护。\(n\)\(m\) 同阶,最终复杂度 \(O(p + n \log n \log p)\)

代码

#include<iostream>
#include<cstdio>
using namespace std;
const int N=20000000;
int n,m;
long long num[500010],c[500010],phi[N+10];
void modify(int pos,long long dif){
	while(pos<=n){
		c[pos]+=dif;
		pos+=pos&-pos; 
	}
}
long long query(int pos){
	long long ans=0;
	while(pos>=1){
		ans+=c[pos];
		pos-=pos&-pos;
	}
	return ans;
}
int tot,prime[N+10];
bool del[N+10];
void init(){
	phi[1]=1;
	for(int i=2;i<=N;i++){
		if(!del[i]){
			prime[++tot]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=tot  &&  prime[j]<=N/i;j++){
			del[i*prime[j]]=true;
			if(i%prime[j]){
				phi[i*prime[j]]=phi[i]*(prime[j]-1);
			}
			else{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
		}
	}
}
long long mod(long long lhs,long long rhs){
	if(lhs>=rhs) return lhs%rhs+rhs;
	else return lhs;
}
long long pow_mod(long long lhs,long long rhs,long long p){
	lhs=mod(lhs,p);
	if(rhs==1) return mod(lhs,p);
	long long ans=pow_mod(lhs,rhs/2,p);
	ans=mod(ans*ans,p);
	if(rhs%2==1) ans=mod(ans*lhs,p);
	return ans;
}
long long ask(long long l,long long r,long long p){
	if(l==r) return mod(query(l),p);
	else if(p==1) return 1;
	else return pow_mod(query(l),mod(ask(l+1,r,phi[p]),phi[p]),p);
}
int main(){
	init();
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
		modify(i,num[i]-num[i-1]);
	}
	while(m--){
		int cmd;
		scanf("%d",&cmd);
		if(cmd==1){
			int l,r;
			long long dif;
			scanf("%d %d %lld",&l,&r,&dif);
			modify(l,dif);
			modify(r+1,-dif);
		}
		else{
			int l,r;
			long long p;
			scanf("%d %d %lld",&l,&r,&p);
			printf("%lld\n",ask(l,r,p)%p);
		}
	}
	return 0;
}
posted @ 2025-06-02 23:02  Oken喵~  阅读(1)  评论(0)    收藏  举报