【题目链接】
【思路要点】
- 欧拉拓展定理:\(a^{\phi(n)}\equiv a^{2\phi(n)}(Mod\ n)\)。
- 而\(\phi(\phi(n))<\frac{n}{2}\)(\(\phi(奇数)=偶数\),\(\phi(偶数)≤\frac{偶数}{2}\)),所以至多有\(O(LogP)\)个不同的模数,模数就会变成1,所以经过\(O(LogP)\)次修改后,被操作数对\(P\)取模的结果都不会再发生变化。
- 因此,我们用一棵线段树维护这个序列,维护区间和以及区间内是否还存在修改之后会变化的数即可。
- 时间复杂度\(O(NLog^3P+MLogN)\)。
- 需要注意的是,模数变成1后,我们需要保留2个1来保证程序的正确性。
- 这是因为我们关注的并不完全是取模的结果,而关注的是\(f(x,p)\)。
- 当\(x≤p\),\(f(x,p)=x\),否则,\(f(x,p)=x\ Mod\ p+p\)。
- 而\(f(x,1)\)的结果不总是1,当\(x=0\),\(f(x,1)=0\)。
- 因此,只保留一个1是不够的,若保留2个1,则有\(f(c^{f(x,1)},1)=1\)恒成立。
- 至此,我们解决了本题。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 50005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, tot, p[MAXN], c, x[MAXN]; struct SegmentTree { struct Node { int lc, rc; int sum, Min; } a[MAXN * 2]; int n, root, size; void update(int root) { a[root].sum = (a[a[root].lc].sum + a[a[root].rc].sum) % p[1]; a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min); } void build(int &root, int l, int r) { root = ++size; if (l == r) { a[root].Min = 1; read(x[l]); a[root].sum = x[l]; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } int power(int x, int y, int P, bool &flg) { if (y == 0) return 1; int tmp = power(x, y / 2, P, flg); if (1ll * tmp * tmp >= P) flg = true; tmp = 1ll * tmp * tmp % P; if (y % 2 == 0) return tmp; else { if (1ll * x * tmp >= P) flg = true; return 1ll * x * tmp % P; } } int calc(int depth, int x) { if (x >= p[depth]) x = x % p[depth] + p[depth]; for (int i = depth - 1; i >= 1; i--) { bool flg = false; x = power(c, x, p[i], flg); if (flg) x += p[i]; } return x % p[1]; } void modify(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr && a[root].Min == tot) return; if (l == r) { a[root].sum = calc(++a[root].Min, x[l]); return; } int mid = (l + r) / 2; if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); update(root); } int query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].sum; int mid = (l + r) / 2, ans = 0; if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(qr, mid)); if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans % p[1]; } void modify(int l, int r) {modify(root, 1, n, l, r); } int query(int l, int r) {return query(root, 1, n, l, r); } } ST; int phi(int x) { int ans = x, tmp = x, now = 2; while (now * now <= tmp) { if (tmp % now == 0) { ans = ans / now * (now - 1); while (tmp % now == 0) tmp /= now; } now++; } if (tmp != 1) ans = ans / tmp * (tmp - 1); return ans; } int main() { tot = 1; read(n), read(m), read(p[tot]), read(c); while (p[tot - 1] != 1) { p[tot + 1] = phi(p[tot]); tot++; } ST.init(n); while (m--) { int opt, l, r; read(opt), read(l), read(r); if (opt == 0) ST.modify(l, r); else writeln(ST.query(l, r)); } return 0; }