【LuoguP3747】[六省联考2017] 相逢是问候
题意
给定一个长度为 n 的序列 a , 给定一个正整数 c
每次修改操作是把一段区间内的数 \(x_i\) 修改为 \(c^{x_i}\)
询问区间和模 p 的结果
Sol
修改是把一个数变成 \(c^x\) , 我们很容易想到降幂公式 , 这里由于 p 不一定与 c 互质 , 那么我们就使用扩展欧拉公式 , 也就是:
\(\varphi(p)\) 函数对于一个数来说 , 做多取 \(O(log_2\ p)\) 次就会变成 1 , 这样我们直接暴力在线段树上修改每一个数 , 直到这个数变成了 \(c^{c^{c^{c^{c^{\dots}}}}}\) , 最后的答案我们可以通过直接使用扩展欧拉定理递归算出 , 这样每一个数也最多被修改 \(log\) 次就不会再变了 , 那么我们总共的单点修改的次数就会是 \(O(nlogn)\) 级别的。
于是我们只需要考虑单点修改。
对于一次单点修改,我们相当于是把原来的数底下塞上一个 c , 由于这一步操作对于每一层模数都变化了, 并不能直接得到, 那么必须每次都要单独算,每次暴力用公式递归进去计算的话复杂度变成了 \(O(nlog^3n)\) 级别的,不能通过所有数据。
考虑优化,我们把快速幂的复杂度优化掉就行了,由于指数的级别最大和 p 差不多,我们对于每一个递归中会遇到模数 \(p\) 处理一个 \(c^x\) 和 \(c^{10000x}\) 就可以 \(O(1)\) 把幂的结果算出来了。
一个比较令人头痛的问题就是判断当前的指数是否大于了当前模数的 欧拉函数值,因为这时我们还要在把指数加上一个 \(\varphi(p)\) ,只需要每次计算的时候先用 \(long\) \(long\) 存好看是否大于模数,用变量记下是否比模数大了就行了。
code:
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
typedef long long ll;
int n,m,p,c;
const int N=5e4+10;
int a[N];
const int MAXN=N<<2;
int hsize[MAXN],dat[MAXN],times[MAXN],sum[MAXN];
#define ls (u<<1)
#define rs (u<<1|1)
int final_ans;
int phi[N],cnt=0;
const int MAXM=2e4;
struct Powerset{
int Mod;bool bom[MAXM];int val[MAXM];
inline void Prepare(int base,int Mo,bool cbom){
Mod=Mo;bom[0]=0;val[0]=1;bom[1]=cbom,val[1]=base;
for(int i=2;i<MAXM;++i) {
ll res=(ll)val[i-1]*base;
bom[i]=bom[i-1];if(res>=Mod) bom[i]=1;
val[i]=res%Mod;
}return;
}
}Po[61][2];
inline int Sum(int x,int y){x+=y;if(x>=p) x-=p;return x;}
inline int Dif(int x,int y){x-=y;if(x< 0) x+=p;return x;}
inline int fpow(int x,int k,int p){int ret=1;for(;k;k>>=1,x=(ll)x*x%p)if(k&1) ret=(ll)ret*x%p;return ret;}
const int INF=2147483647;
void Build(int u,int l,int r){
if(l==r) {dat[u]=a[l],hsize[u]=1,sum[u]=a[l],times[u]=0;return;}
int mid=l+r>>1;
Build(ls,l,mid),Build(rs,mid+1,r);
hsize[u]=r-l+1;sum[u]=Sum(sum[ls],sum[rs]);
}
inline int Get_phi(int x){int ret=x;
for(int i=2;i*i<=x;++i) {if(x%i==0) {ret-=ret/i;x/=i;while(x%i==0)x/=i;}}
if(x>1) ret-=ret/x;return ret;
}
inline int Calc(int p){
if(p==1) return 0;int nowphi=Get_phi(p);
int ret=fpow(c,Calc(nowphi)+nowphi,p)%p;
phi[++cnt]=nowphi;return ret;
}
inline int Fpow(int x,int k,int P){
bool boom=0,boompow=0;int ret=1;
while(k){
if(k&1) {ll res=(ll)ret*x;if(res>=P) boom=1;boom|=boompow;ret=res%P;}
boompow=0;ll res=(ll)x*x;
if(res>=P) boompow=1;x=res%P;k>>=1;
}if(boom) ret+=P;return ret;
}
inline int Solve(int t,int a,int id){
if(!t) return a>phi[id]? (a%phi[id]+phi[id]):a;
int Dat=Solve(t-1,a,id+1);
if(!id) return (ll)Po[id][0].val[Dat%MAXM]*Po[id][1].val[Dat/MAXM]%phi[id];
else {
int r1=Dat%MAXM,r2=Dat/MAXM;
bool bm=Po[id][0].bom[r1]|Po[id][1].bom[r2];
if(bm) return (ll)Po[id][0].val[r1]*Po[id][1].val[r2]%phi[id]+phi[id];
else return (ll)Po[id][0].val[r1]*Po[id][1].val[r2]%phi[id];
}
}
inline void Modify(int u,int l,int r,int L,int R){
if(!hsize[u]) return;
if(l>=L&&r<=R) {
if(l==r) {++times[u];
if(times[u]>cnt) sum[u]=final_ans,hsize[u]=0;
else sum[u]=Solve(times[u],dat[u],0)%p;
return;
}int mid=l+r>>1;
Modify(ls,l,mid,L,R);Modify(rs,mid+1,r,L,R);
sum[u]=Sum(sum[ls],sum[rs]);hsize[u]=hsize[ls]+hsize[rs];
return;
}int mid=l+r>>1;
if(mid>=L) Modify(ls,l,mid,L,R);
if(mid< R) Modify(rs,mid+1,r,L,R);
sum[u]=Sum(sum[ls],sum[rs]);
hsize[u]=hsize[ls]+hsize[rs];
}
inline int Query(int u,int l,int r,int L,int R){
if(l>=L&&r<=R) return sum[u];
int mid=l+r>>1;
if(mid>=R) return Query(ls,l,mid,L,R);if(mid<L) return Query(rs,mid+1,r,L,R);
return Sum(Query(ls,l,mid,L,mid),Query(rs,mid+1,r,mid+1,R));
}
namespace Fun{
bool tag[MAXN];
inline void set1(int u,int l,int r){sum[u]=r-l+1;tag[u]=1;return;}
inline void push_down(int u,int l,int mid,int r){if(!tag[u]) return;set1(ls,l,mid),set1(rs,mid+1,r),tag[u]=0;}
inline void Modify(int u,int l,int r,int L,int R){
if(l>=L&&r<=R) return set1(u,l,r);
int mid=l+r>>1;push_down(u,l,mid,r);
if(mid>=L) Modify(ls,l,mid,L,R);if(mid<R) Modify(rs,mid+1,r,L,R);
sum[u]=Sum(sum[ls],sum[rs]);
}
inline int Query(int u,int l,int r,int L,int R){
if(l>=L&&r<=R) return sum[u];
int mid=l+r>>1;push_down(u,l,mid,r);
if(mid>=R) return Query(ls,l,mid,L,R);if(mid<L) return Query(rs,mid+1,r,L,R);
return Sum(Query(ls,l,mid,L,mid),Query(rs,mid+1,r,mid+1,R));
}
void work(){
for(int i=1;i<=m;++i) {
int op,l,r;init(op),init(l),init(r);
if(op==0) Modify(1,1,n,l,r);
else printf("%d\n",Query(1,1,n,l,r)%p);
}
}
}
inline void Prework(){
for(int i=0;i<cnt;++i) {
Po[i][0].Prepare(c,phi[i],c>=phi[i]);
Po[i][1].Prepare(fpow(c,2e4,phi[i]),phi[i],1);
}return;
}
int main()
{
init(n),init(m),init(p),init(c);
for(int i=1;i<=n;++i) init(a[i]);
Build(1,1,n);
if(c==1) Fun::work();
else {
phi[0]=p;final_ans=Calc(p);
reverse(phi+1,phi+1+cnt);
Prework();
for(int i=1;i<=m;++i) {
int op,l,r;init(op),init(l),init(r);
if(op==0) Modify(1,1,n,l,r);
else printf("%d\n",Query(1,1,n,l,r)%p);
}
}
return 0;
}