傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=4869
【題解】
發現好像沒有辦法普通維護。
給小盆友們江數論的時候江過x^m mod p = x^(m mod phi(p)) mod p
upd: 這篇文章寫的時候我也不知道我自己怎么想的了 好像上面這個有點問題。。但是還是AC了(逃
下面給出擴展歐拉定理的證明:https://zhuanlan.zhihu.com/p/24902174
以及,該定理的正確表示$x^m \equiv x^{m~mod~phi(p) + [m \geq phi(p)] phi(p)}(mod~p)$
注意當$m < phi(p)$的時候是不用加上$phi(p)$的。
發現一個數進行phi操作最多log次。
暴力就行啦qwq
注意就是phi的那個最后要補一個phi[++pn] = 1。
為什么呢?因為phi(1) = 1(展開到最后,還要多展開一層(!))
那么就行啦!
復雜度不大會分析qwq
照理性分析可能是log方到log三方之間的啦
反正跑的……挺快
upd: 兩份代碼均已經更正。 感謝Exbilar的提醒。

# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; # define RG register # define ST static int n, m, p, c, a[M], phi[M], pn; inline int pwr(int a, int b, int p, bool &ok) { ok = 0; int ret = 1; while(b) { if(b&1) ok |= (1ll * ret * a >= p), ret = 1ll * ret * a % p; ok |= (1ll * a * a >= p && b != 1); a = 1ll * a * a % p; b >>= 1; } return ret; } inline int getphi(int n) { int ret = n; for (int i=2; i*i<=n; ++i) { if(n%i!=0) continue; ret = ret/i*(i-1); while(n%i==0) n/=i; } if(n!=1) ret=ret/n*(n-1); return ret; } inline int calc(int x, int p) { int ret = x; bool ok; if(ret >= phi[p]) ret = ret % phi[p] + phi[p]; while(p--) { x = ret; ret = pwr(c, x, phi[p], ok); if(ok) ret += phi[p]; } return ret % phi[0]; } namespace SMT { # define ls (x<<1) # define rs (x<<1|1) int v[M], tms[M]; inline void build(int x, int l, int r) { if(l==r) { v[x] = a[l] % phi[0]; tms[x] = 0; return ; } int mid = l+r>>1; build(ls, l, mid); build(rs, mid+1, r); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline void change(int x, int l, int r, int L, int R) { if(tms[x] >= pn) return; if(l == r) { tms[x] ++; v[x] = calc(a[l], tms[x]); return ; } int mid = l+r>>1; if(L <= mid) change(ls, l, mid, L, R); if(R > mid) change(rs, mid+1, r, L, R); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline int query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return v[x]; int mid = l+r>>1, re=0; if(L <= mid) re += query(ls, l, mid, L, R); if(R > mid) re += query(rs, mid+1, r, L, R); return re%phi[0]; } # undef ls # undef rs } int main() { scanf("%d%d%d%d", &n, &m, &p, &c); for (int i=1; i<=n; ++i) scanf("%d", a+i); phi[0] = p; while(p != 1) { int t = getphi(p); phi[++pn] = t; p = t; } phi[++pn] = 1; SMT::build(1, 1, n); int opt, l, r; while(m--) { scanf("%d%d%d", &opt, &l, &r); if(opt==0) SMT::change(1, 1, n, l, r); else printf("%d\n", SMT::query(1, 1, n, l, r)); } return 0; }
我們發現我們可以把快速冪的一個log強行壓掉 這樣就會快很多啦qwq
部分代碼實現by zhouyi

# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; # define RG register # define ST static int n, m, p, c, a[M], phi[M], pn; int pws[27][M][2]; bool pwk[27][M][2]; inline int pwr(int a, int b, int p, bool &ok) { ok = 0; int ret = 1; while(b) { if(b&1) ok |= (1ll * ret * a >= p), ret = 1ll * ret * a % p; ok |= (1ll * a * a >= p && b != 1); a = 1ll * a * a % p; b >>= 1; } return ret; } inline int getphi(int n) { int ret = n; for (int i=2; i*i<=n; ++i) { if(n%i!=0) continue; ret = ret/i*(i-1); while(n%i==0) n/=i; } if(n!=1) ret=ret/n*(n-1); return ret; } inline int pwrb(int b, int p, bool &ok){ p = min(p, pn); ok = 0; ok |= pwk[p][b & 16383][0]; ok |= pwk[p][b >> 14][1]; ok |= (1ll * pws[p][b & 16383][0] * pws[p][b >> 14][1] >= phi[p]); return 1ll * pws[p][b & 16383][0] * pws[p][b >> 14][1] % phi[p]; } inline int calc(int x, int p) { int ret = x; bool ok; if(ret >= phi[p]) ret = ret % phi[p] + phi[p]; while(p--) { x = ret; ret = pwrb(x, p, ok); if(ok) ret += phi[p]; } return ret % phi[0]; } namespace SMT { # define ls (x<<1) # define rs (x<<1|1) int v[M], tms[M]; inline void build(int x, int l, int r) { if(l==r) { v[x] = a[l] % phi[0]; tms[x] = 0; return ; } int mid = l+r>>1; build(ls, l, mid); build(rs, mid+1, r); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline void change(int x, int l, int r, int L, int R) { if(tms[x] >= pn) return; if(l == r) { tms[x] ++; v[x] = calc(a[l], tms[x]); return ; } int mid = l+r>>1; if(L <= mid) change(ls, l, mid, L, R); if(R > mid) change(rs, mid+1, r, L, R); v[x] = (v[ls]+v[rs])%phi[0]; tms[x] = min(tms[ls], tms[rs]); } inline int query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) return v[x]; int mid = l+r>>1, re=0; if(L <= mid) re += query(ls, l, mid, L, R); if(R > mid) re += query(rs, mid+1, r, L, R); return re%phi[0]; } # undef ls # undef rs } int main() { scanf("%d%d%d%d", &n, &m, &p, &c); for (int i=1; i<=n; ++i) scanf("%d", a+i); phi[0] = p; while(p != 1) { int t = getphi(p); phi[++pn] = t; p = t; } phi[++pn] = 1; for(int i=0; i<=pn; ++i) { bool ok; int j = pwr(c, 16384, phi[i], ok); pws[i][0][0] = pws[i][0][1] = 1; pwk[i][0][0] = 0, pwk[i][0][1] = 0; for(int k=1; k<16384; k++) pwk[i][k][0] = pwk[i][k-1][0] | (1ll * pws[i][k-1][0] * c >= phi[i]), pws[i][k][0] = 1ll * pws[i][k-1][0] * c % phi[i], pwk[i][k][1] = pwk[i][k-1][1] | (ok || (1ll * pws[i][k-1][1] * j >= phi[i])), pws[i][k][1] = 1ll * pws[i][k-1][1] * j % phi[i]; } SMT::build(1, 1, n); int opt, l, r; while(m--) { scanf("%d%d%d", &opt, &l, &r); if(opt==0) SMT::change(1, 1, n, l, r); else printf("%d\n", SMT::query(1, 1, n, l, r)); } return 0; }