1. 程式人生 > >[BZOJ 4809] 相逢是問候

[BZOJ 4809] 相逢是問候

就會 type esp 做到 define std else shu 當前

Link:

傳送門

Solution:

以前沒見過的套路題……

1、使用EXT歐拉定理降冪的套路:

$a^{x}=a^{xmod\phi(P)+\phi(P)} mod P$,$x\ge P$

這樣對於$c^{c^{c^x}}modP$就能遞推/遞歸得套用上述定理計算,每層模數多套一層$\phi$即可

註意每次在快速冪時要判斷當前指數是否大於當前模數才能用EXT!

2、能證明一個數最多求$log$次$\phi$就會變成1

這樣在$log$次內暴力更新,否則不管,就能保證$O(n*log^3)$

3、復雜度中的3個$log$分別是:

更新$log$次,每次更新叠代$log$層,每層要算一次快速冪

明顯只能優化快速冪。由於底不變,模數只有$log$種,想到分數的前後兩部分預處理

分塊預處理出$[1,(1<<16)]$和$[1*(1<<16),(1<<16)*(1<<16)]$的答案以及與模數的大小關系

這樣每次拆出指數的前16位和後16位$O(1)$計算答案和大小關系就能做到$O(n*log^2)$

4、聽說原題數據鍋了……

雖然$\phi(2)$和$\phi(1)$都為1,但要更新到$\phi(1)$!

否則在最頂層指數為0時最終會叠代出$cmod2$而非$cmod1$,不一定為0!

Code:

#include <bits/stdc++.h>

using
namespace std; #define X first #define Y second #define pb push_back typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10; ll pre[35][1<<16][2]; bool f[35][1<<16][2]; int n,m,p,c,dat[MAXN],phi[35],cnt; int getphi(int x) { int ret=x; for(int
i=2;i*i<=x;i++) if(x%i==0) { ret=ret/i*(i-1); while(x%i==0) x/=i; } if(x!=1) ret=ret/x*(x-1); return ret; } ll quick_pow(ll a,ll b,ll MOD,bool &f) { ll ret=1; for(;b;b>>=1,a=a*a%MOD) { //判斷是否大於某個數兩個地方都要判! if(b&1) f|=(ret*a>=MOD),ret=ret*a%MOD; f|=(a*a>=MOD&&b!=1); } return ret; } int QP(int x,int num,bool &flag) { int a=x&((1<<16)-1),b=x>>16; ll ret=1ll*pre[num][a][0]*pre[num][b][1]; flag=f[num][a][0]|f[num][b][1]|(ret>=phi[num]); return ret%phi[num]; } int cal(int x,int st) { int ret=x; if(ret>=phi[st]) ret=ret%phi[st]+phi[st]; while(st--) { bool f=0; ret=QP(ret,st,f); //註意特判,僅在指數>模數時可使用EXT歐拉定理 if(f&&st) ret+=phi[st]; } return ret%p; } void PRE() { //分塊預處理 for(int i=0;i<=cnt;i++) { pre[i][0][0]=pre[i][0][1]=1; if(phi[i]==1) f[i][0][0]=f[i][0][1]=1; pre[i][1][0]=quick_pow(c,1,phi[i],f[i][1][0]); int tmp=pre[i][1][1]=quick_pow(c,1<<16,phi[i],f[i][1][1]); for(int j=2;j<1<<16;j++) { f[i][j][0]=f[i][j-1][0]|(pre[i][j-1][0]*c>=phi[i]); pre[i][j][0]=pre[i][j-1][0]*c%phi[i]; f[i][j][1]=f[i][j-1][1]|(pre[i][j-1][1]*tmp>=phi[i]); pre[i][j][1]=pre[i][j-1][1]*tmp%phi[i]; } } } namespace SegmentTree { #define mid ((l+r)>>1) #define ls k<<1 #define rs k<<1|1 #define lc ls,l,mid #define rc rs,mid+1,r int sum[MAXN<<2],tag[MAXN<<2]; void pushup(int k) { tag[k]=min(tag[ls],tag[rs]); sum[k]=(sum[ls]+sum[rs])%p; } void build(int k,int l,int r) { if(l==r) {sum[k]=dat[l]%p;tag[k]=0;return;} build(lc);build(rc);pushup(k); } void modify(int a,int b,int k,int l,int r) { if(tag[k]>=cnt) return; if(l==r) {sum[k]=cal(dat[l],++tag[k]);return;} if(a<=mid) modify(a,b,lc); if(b>mid) modify(a,b,rc); pushup(k); } int query(int a,int b,int k,int l,int r) { if(a<=l&&r<=b) return sum[k]; int ret=0; if(a<=mid) (ret+=query(a,b,lc))%=p; if(b>mid) (ret+=query(a,b,rc))%=p; return ret; } } using namespace SegmentTree; int main() { scanf("%d%d%d%d",&n,&m,&p,&c); for(int i=1;i<=n;i++) scanf("%d",&dat[i]); phi[0]=p; while(phi[cnt]!=1) cnt++,phi[cnt]=getphi(phi[cnt-1]); //要叠代到phi(1)=1,而不能僅叠代到phi(2)=1 //否則在x=0時最後一層會出現c%2 phi[++cnt]=1;PRE(); build(1,1,n); while(m--) { int op,l,r; scanf("%d%d%d",&op,&l,&r); if(!op) modify(l,r,1,1,n); else printf("%d\n",query(l,r,1,1,n)); } return 0; }

[BZOJ 4809] 相逢是問候