BZOJ4869: [Shoi2017]相逢是问候
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; }