「SHOI2017」相逢是问候
这道题真的难受,本来一眼望过去以为随便写写就60分了,结果发现<100那块部分分拿不了拿不了,因为修改过程中是不能直接对p取模的。一般想到那块部分分怎么做正解也就出来了。
首先了解一下扩展欧拉定理,对于a^b%p,当gcd(a,p)=1的时候a^b%p=a^(b%phi[p])%p,当gcd(a,p)不为1的时候,若b<phi[p],则a^b%p=a^b%p,不然a^b%p=a^(b%phi[p]+phi[p])%p。
那么看一下这道题实质上是在求什么,修改一次变为c^a,两次变为c^(c^a),三次变为c^(c^(c^a))).......
如果求c^(c^a)%p可以通过扩展欧拉定理变换为c^(c^a%phi[p]+(c^a>=phi[p])*phi[p])%p,我们发现里面又变成了一个子问题,即求c^a%phi[p],此时已经可以直接求解了,如果修改更多次,可以重复这个过程,那么就是对模数不停的取phi,我们发现,取得次数达到一定次数的时候,这个值将不会再改变,因为当某一层phi为1的时候,如果再次修改,到达phi=1那一层,值必然会变成0,无论怎样递归下去,到达此层的值已经固定了。但是存在一种特殊情况,即a=0的时候,这个时候当phi=1了,我们还需要再进行一次才能保证值会不变,因为0<1,而再进行一次操作之后的值=1,那么对于(c^a>=phi[p])*phi[p]这一部分,他们会得到不同的情况,所以需要在phi=1之后额外进行一次,而且只需要进行一次,因为之后再也不会出现=0的情况了。可以证明,进行log级别次操作后phi会变成1,虽然我不会证明,可以当作结论来记来记吧。那么就可以类似于区间开平方一样,修改时暴力修改,并标记是否已经全部修改到上限了。分块和线段树均可,并且复杂度相同,因为此题瓶颈在于修改的nlog^3,但是这样似乎过不去,还需要对快速幂进行优化,因为从始至终,都是在对c进行快速幂,所以可以预处理出来c^(1...1<<16)和c^((1<<16)*(1...1<<16))的值,O1的求解。
当然此题有一个小问题,也是纠结了我好几天的问题,就是我们判断c^()幂是否大于模数的时候,是不能用其模意义下的值来比较的,但是事实上这样做并不会有影响,听说有人验证过,然而我也不清楚这个。


1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int inf=5e4+10; 5 int n,m,p,c; 6 int limit,phi[inf]; 7 int a[inf]; 8 int get_phi(int x){ 9 int ans=x; 10 for(int i=2;i*i<=x;i++){ 11 if(x%i)continue; 12 ans=ans/i*(i-1); 13 while(x%i==0)x/=i; 14 } 15 if(x>1)ans=ans/x*(x-1); 16 return ans; 17 } 18 int pow1[100][1<<16],pow2[100][1<<16],Cnt[100]; 19 int fast(int x,int y){ 20 if(c==1)return 1; 21 if(x<Cnt[y])return pow1[y][x]; 22 int u=x>>16; 23 return 1ll*pow1[y][x-(u<<16)]*pow2[y][u]%phi[y]+phi[y]; 24 } 25 int get(int x,int y){ 26 int ans=x; 27 for(int i=y-1;i>=0;i--) 28 ans=fast(ans,i); 29 return ans%p; 30 } 31 int b[inf]; 32 int len,pos[inf],l[inf],r[inf]; 33 int sum[inf],cnt[inf],mn[inf]; 34 void modify(int x,int y){ 35 for(int i=x;i<=min(y,r[pos[x]]);i++){ 36 if(cnt[i]==limit)continue; 37 cnt[i]++; 38 sum[pos[i]]-=b[i]; 39 b[i]=get(a[i],cnt[i]); 40 sum[pos[i]]+=b[i]; 41 sum[pos[i]]%=p; 42 sum[pos[i]]=(sum[pos[i]]+p)%p; 43 } 44 mn[pos[x]]=0x3fffffff; 45 for(int i=l[pos[x]];i<=r[pos[x]];i++) 46 mn[pos[i]]=min(mn[pos[i]],cnt[i]); 47 for(int i=pos[x]+1;i<pos[y];i++){ 48 if(mn[i]==limit)continue; 49 mn[i]=0x3fffffff; 50 for(int j=l[i];j<=r[i];j++){ 51 if(cnt[j]==limit)continue; 52 cnt[j]++; 53 mn[i]=min(mn[i],cnt[j]); 54 sum[i]-=b[j]; 55 b[j]=get(a[j],cnt[j]); 56 sum[i]+=b[j]; 57 sum[i]%=p; 58 sum[i]=(sum[i]+p)%p; 59 } 60 } 61 if(pos[x]!=pos[y]){ 62 for(int i=l[pos[y]];i<=y;i++){ 63 if(cnt[i]==limit)continue; 64 cnt[i]++; 65 sum[pos[i]]-=b[i]; 66 b[i]=get(a[i],cnt[i]); 67 sum[pos[i]]+=b[i]; 68 sum[pos[i]]%=p; 69 sum[pos[i]]=(sum[pos[i]]+p)%p; 70 } 71 mn[pos[y]]=0x3fffffff; 72 for(int i=l[pos[y]];i<=r[pos[y]];i++) 73 mn[pos[i]]=min(mn[pos[i]],cnt[i]); 74 } 75 } 76 int query(int x,int y){ 77 int ans=0; 78 for(int i=x;i<=min(r[pos[x]],y);i++) 79 ans=(ans+b[i])%p; 80 for(int i=pos[x]+1;i<pos[y];i++) 81 ans=(ans+sum[i])%p; 82 if(pos[x]!=pos[y]) 83 for(int i=l[pos[y]];i<=y;i++) 84 ans=(ans+b[i])%p; 85 return ans; 86 } 87 int main() 88 { 89 scanf("%d%d%d%d",&n,&m,&p,&c); 90 for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i]; 91 phi[0]=p; 92 while(phi[limit]!=1) 93 phi[++limit]=get_phi(phi[limit-1]); 94 phi[++limit]=1; 95 for(int i=0;i<=limit;i++){ 96 pow1[i][0]=1%phi[i]; 97 for(int j=1;j<(1<<16);j++) 98 pow1[i][j]=1ll*pow1[i][j-1]*c%phi[i]; 99 int u=1ll*pow1[i][(1<<16)-1]*c%phi[i]; 100 pow2[i][0]=1%phi[i]; 101 for(int j=1;j<(1<<16);j++) 102 pow2[i][j]=1ll*pow2[i][j-1]*u%phi[i]; 103 LL now=1; 104 while(c!=1 && now<phi[i])now=now*c,Cnt[i]++; 105 } 106 len=sqrt(n); 107 for(int i=1;i<=n;i++) 108 sum[pos[i]=(i-1)/len+1]+=a[i], 109 sum[pos[i]]%=p; 110 for(int i=1;i<=n;i++){ 111 if(pos[i]!=pos[i-1])l[pos[i]]=i; 112 if(pos[i]!=pos[i+1])r[pos[i]]=i; 113 } 114 for(int i=1;i<=m;i++){ 115 int opt,x,y; 116 scanf("%d%d%d",&opt,&x,&y); 117 if(opt) 118 printf("%d\n",query(x,y)); 119 else 120 modify(x,y); 121 } 122 return 0; 123 }