Description
Informatikverbindetdichundmich.
信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
这个结果可能会很大,所以你只需要输出结果mod p的值即可。
Input
第一行有三个整数n,m,p,c,所有整数含义见问题描述。
接下来一行n个整数,表示a数组的初始值。
接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
如果是0的话,表示这是一个修改操作,操作的参数为l,r。
如果是1的话,表示这是一个询问操作,操作的参数为l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p
Output
对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。
Sample Input
4 4 7 2
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
Sample Output
0
3
3
题解Here!
首先,有个题建议先做一做:
BZOJ3884: 上帝与集合的正确用法
然后你会发现你已经会了本题的重要思想:
$$c^{c^{c^{...}}}\equiv c^{(c^{c^{...}}\mod \varphi(p)+\varphi(p))}(\mod p)$$
当然,那个前提还是没变:$c^{c^{...}}>\varphi(p)$
然后递归计算即可。
但是问题又出来了,区间操作怎么办?
不怕!我们还有一大堆数据结构没用呢!
当然前提是有个题建议做一做:
BZOJ3211: 花神游历各国
然后我们就可以类似地发现:当指数层数达到一定数量时,$c^{c^{c^{...}}}\mod p$的值不再变化。
所以直接线段树暴力单点修改,达到临界条件就打个标记丢一边不管了。
可是,这玩意写完了,我们发现这玩意的复杂度惊人啊:$O(m\log_2n\log_2^2p)$
这个怎么办呢?
不怕,我们拿出终极优化——欧拉函数很少!
这个有什么用?
这意味着模数很少!
所以我们可以考虑将快速幂预处理一下,分成$c^i\mod p$和$c^{10000\times i}\mod p$两部分。
查询的时候就直接把两块合并就好。
但是!欧拉定理的运用是有限制条件的!
所以我们还需要一个$bool$数组来记录这个条件是否满足。
于是我们省去了一个$\log_2p$。
总复杂度$O(m\log_2n\log_2p)$,可以通过此题。
附上奇丑无比的代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define SIGN(x) a[x].times
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define MAXN 50010
#define MAXM 60
using namespace std;
int n,m,base,mod,min_times=0;
long long val[MAXN],phi[MAXM],pow_one[MAXN/5][MAXM],pow_two[MAXN/5][MAXM];
bool flag,flag_one[MAXN/5][MAXM],flag_two[MAXN/5][MAXM];
struct Segment_Tree{
long long data;
int times,l,r;
}a[MAXN<<2];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline long long min(const long long &x,const long long &y){return x<y?x:y;}
long long mexp(long long a,long long b,long long c){
long long s=1;
while(b){
if(b&1)s=s*a%c;
a=a*a%c;
b>>=1;
}
return s;
}
long long get_phi(long long x){
long long limit=sqrt(x),s=x;
for(long long i=2;i<=limit;i++){
if(x%i==0){
s=s*(i-1)/i;
while(x%i==0)x/=i;
}
}
if(x>1)s=s*(x-1)/x;
return s;
}
void make(){
int limit=10000;
long long x=mod;
phi[0]=mod;
while(x!=1){
x=get_phi(x);
phi[++min_times]=x;
}
phi[++min_times]=1;
for(int i=0;i<=min_times;i++){
pow_one[0][i]=1;
for(int j=1;j<=limit;j++){
pow_one[j][i]=pow_one[j-1][i]*base;
if(pow_one[j][i]>=phi[i]){
pow_one[j][i]%=phi[i];
flag_one[j][i]=true;
}
flag_one[j][i]|=flag_one[j-1][i];
}
}
for(int i=0;i<=min_times;i++){
pow_two[0][i]=1;
flag_two[1][i]=flag_one[limit][i];
for(int j=1;j<=limit;j++){
pow_two[j][i]=pow_two[j-1][i]*pow_one[limit][i];
if(pow_two[j][i]>=phi[i]){
pow_two[j][i]%=phi[i];
flag_two[j][i]=true;
}
flag_two[j][i]|=flag_two[j-1][i];
}
}
}
inline long long calculate(long long x,long long v){
flag=false;
long long p=x%10000,q=x/10000,s=pow_one[p][v]*pow_two[q][v];
if(s>=phi[v]){
s%=phi[v];
flag=true;
}
flag|=flag_one[p][v]|flag_two[q][v];
return s;
}
long long solve(long long x,int deep,int limit){
flag=false;
if(deep==limit){
if(x>=phi[deep]){
flag=true;
x%=phi[deep];
}
return x;
}
long long s=solve(x,deep+1,limit);
return calculate((flag?(s+phi[deep+1]):s),deep);
}
inline void pushup(int rt){
DATA(rt)=(DATA(LSON)+DATA(RSON))%mod;
SIGN(rt)=min(SIGN(LSON),SIGN(RSON));
}
void buildtree(int l,int r,int rt){
LSIDE(rt)=l;RSIDE(rt)=r;
if(l==r){
DATA(rt)=val[l];
SIGN(rt)=0;
return;
}
int mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
pushup(rt);
}
void update(int l,int r,int rt){
if(SIGN(rt)>=min_times)return;
if(LSIDE(rt)==RSIDE(rt)){
SIGN(rt)++;
DATA(rt)=solve(val[LSIDE(rt)],0,SIGN(rt));
return;
}
int mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid&&SIGN(LSON)<min_times)update(l,r,LSON);
if(mid<r&&SIGN(RSON)<min_times)update(l,r,RSON);
pushup(rt);
}
long long query(int l,int r,int rt){
long long ans=0;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
int mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)ans=(ans+query(l,r,LSON))%mod;
if(mid<r)ans=(ans+query(l,r,RSON))%mod;
return ans;
}
void work(){
int f,x,y;
while(m--){
f=read();x=read();y=read();
if(f==0)update(x,y,1);
else printf("%lld\n",query(x,y,1));
}
}
void init(){
n=read();m=read();mod=read();base=read();
for(int i=1;i<=n;i++)val[i]=read();
buildtree(1,n,1);
make();
}
int main(){
init();
work();
return 0;
}