题解:P3747 [六省联考 2017] 相逢是问候
前置知识
- 扩展欧拉定理
特别地,当
- P4139 上帝与集合的正确用法
递归
注意由于无穷幂塔是递归无线层的,所以肯定每一层的指数都肯定
int tower(int a,int n,int p){
if(p==1) return 0;
int t = phi(p);
else return qpow(a,tower(a,t)+t,p);
}
本题
观察
这是一个幂塔不断叠的过程,但是注意由于不是无限递归,所以其指数并不一定
int tower(int i,int k,int id){
if(id==lim) return 0;
if(k==0) return a[i]%phi[id];
if(C==1||(k-1<=up[i]&&p[k-1][i]<phi[id+1])) return qpow(tower(i,k-1,id+1),id);
else return qpow(tower(i,k-1,id+1)+phi[id+1],id);
}
这是一个求幂塔的基本代码,其中
关于上一段中的后续指数的大小,也就是我之前说的需要对于每个
其中 tower 函数中的 qpow 如果直接计算,会多一个
void init(){
phi[lim=1]=P;
while(phi[lim]>1) phi[lim+1]=getphi(phi[lim]),lim++;
for(int i=1;i<=lim;i++){
p1[i][0]=1;
for(int j=1;j<=B;j++) p1[i][j]=1ll*p1[i][j-1]*C%phi[i];
int z=p1[i][B]; p2[i][0]=1;
for(int j=1;j*B<=2*P;j++) p2[i][j]=1ll*p2[i][j-1]*z%phi[i];
}
int z=0; c[z]=1;
while(c[z]<P&&z<=30) z++,c[z]=c[z-1]*C;
z--;
for(int i=1;i<=n;i++){
p[up[i]=0][i]=a[i];
while(up[i]<=z&&p[up[i]][i]<=z) p[up[i]+1][i]=p1[1][p[up[i]][i]],up[i]++;
}
}
这是预处理的代码,一段是求出所有
有了以上的基础,我们已经会处理题目中的数论操作了,但是我们需要使用一个高效的结构来维护。
这里采用线段树,线段树可以轻松完成区间求和的任务。一般遇到区间修改,线段树都是把所要求的区间
时间复杂度
#include<bits/stdc++.h>
#define pb emplace_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn=4e5+10;
const int B=1e4;
const int maxlog=40;
void cmax(int &x,int y){ x=x>y?x:y; }
void cmin(int &x,int y){ x=x<y?x:y; }
int a[maxn],phi[maxn],p[maxlog][maxn],P,C,n; ll c[maxlog];
int up[maxn],p1[maxlog][maxn],p2[maxlog][maxn],lim,m;
int qpow(int k,int id){ return 1ll*p1[id][k%B]*p2[id][k/B]%phi[id]; }
int tower(int i,int k,int id){
if(id==lim) return 0;
if(k==0) return a[i]%phi[id];
if(C==1||(k-1<=up[i]&&p[k-1][i]<phi[id+1])) return qpow(tower(i,k-1,id+1),id);
else return qpow(tower(i,k-1,id+1)+phi[id+1],id);
}
struct SegmentTree{
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
ll sum[maxn<<2]; int cnt[maxn<<2];
void pushup(int p){ sum[p]=(sum[ls]+sum[rs])%P; cnt[p]=min(cnt[ls],cnt[rs]); }
void build(int p,int l,int r){
if(l==r){ sum[p]=a[l]; cnt[p]=0; return ; }
build(ls,l,mid); build(rs,mid+1,r); pushup(p);
}
void modify(int p,int l,int r,int ql,int qr){
if(cnt[p]>=lim) return ;
if(l==r){
sum[p]=tower(l,cnt[p]+1,1);
cnt[p]++; return ;
}
if(ql<=mid) modify(ls,l,mid,ql,qr);
if(qr>mid) modify(rs,mid+1,r,ql,qr);
pushup(p);
}
ll query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return sum[p];
if(qr<=mid) return query(ls,l,mid,ql,qr);
if(ql>mid) return query(rs,mid+1,r,ql,qr);
return (query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr))%P;
}
}seg;
int getphi(int x){
int res=x;
for(int i=2;1ll*i*i<=x;i++){
if(x%i) continue;
res=1ll*res*(i-1)/i;
while(x%i==0) x/=i;
}
if(x>1) res=1ll*res*(x-1)/x;
return res;
}
void init(){
phi[lim=1]=P;
while(phi[lim]>1) phi[lim+1]=getphi(phi[lim]),lim++;
for(int i=1;i<=lim;i++){
p1[i][0]=1;
for(int j=1;j<=B;j++) p1[i][j]=1ll*p1[i][j-1]*C%phi[i];
int z=p1[i][B]; p2[i][0]=1;
for(int j=1;j*B<=2*P;j++) p2[i][j]=1ll*p2[i][j-1]*z%phi[i];
}
int z=0; c[z]=1;
while(c[z]<P&&z<=30) z++,c[z]=c[z-1]*C;
z--;
for(int i=1;i<=n;i++){
p[up[i]=0][i]=a[i];
while(up[i]<=z&&p[up[i]][i]<=z) p[up[i]+1][i]=p1[1][p[up[i]][i]],up[i]++;
}
}
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>P>>C;
for(int i=1;i<=n;i++) cin>>a[i];
init(); seg.build(1,1,n);
for(int i=1;i<=m;i++){
int t,l,r; cin>>t>>l>>r;
if(!t) seg.modify(1,1,n,l,r);
else cout<<seg.query(1,1,n,l,r)<<"\n";
}
return 0;
}