P3747 [六省聯考 2017] 相逢是問候 題解
阿新 • • 發佈:2021-12-22
Description
Solution
不打算詳細寫了,簡單寫寫做題歷程。
一看題,顯然要用線段樹維護,但是 \(c^{a_i}\) 這東西怎存??
於是看了一眼標籤,發現 尤拉公式
這個東西,於是想到尤拉定理。
再聯想到區間開方的操作只有前 \(\sqrt n\) 次有用。發現這東西只有前 \(\log\) 次操作有用,所以直接暴力維護即可。
另外 \(c^x\) 次方要提前預處理出來,不然會多一個 \(\log\)。
其他的見程式碼吧,有一點註釋。
這道題的思路還是非常有啟發性的。
Code
\[\_EOF\_ \]#include <bits/stdc++.h> #define ll long long #define ls rt << 1 #define rs rt << 1 | 1 using namespace std; namespace IO{ inline int read(){ int x = 0; char ch = getchar(); while(!isdigit(ch)) ch = getchar(); while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } template <typename T> inline void write(T x){ if(x > 9) write(x / 10); putchar(x % 10 + '0'); } } using namespace IO; const int N = 5e4 + 10; int n, m, mod, c; int a[N][60]; namespace Prework{ int c1[60][1 << 15], c2[60][1 << 15], phi[60]; int totp = 0; inline int add(int x) {return x >= mod ? x -= mod : x;} inline int times(ll x, int mod) {return x >= mod ? (x % mod + mod) : x;} inline int power(int x, int i) {return times(1ll * c1[i][x & ((1 << 15) - 1)] * c2[i][x >> 15], phi[i]);} //單點計算 phi inline int calc_phi(int x){ int res = x; for(int i = 2; i * i <= x; ++i){ if(x % i == 0){ res = res / i * (i - 1); while(x % i ==0) x /= i; } } if(x > 1) res = res / x * (x - 1); return res; } //預處理 (c^x) % phi[i],分成兩半(更快),對於每個 phi[i] 都要預處理 inline void calc_c(){ for(int i = 0; i <= totp; ++i){ c1[i][0] = c2[i][0] = 1; for(int j = 1; j < (1 << 15); ++j) c1[i][j] = times(1ll * c1[i][j - 1] * c, phi[i]); c2[i][1] = times(1ll * c1[i][(1 << 15) - 1] * c, phi[i]); for(int j = 2; j < (1 << 15); ++j) c2[i][j] = times(1ll * c2[i][j - 1] * c2[i][1], phi[i]); } } //預處理出 a[i] 每次操作之後數是多少 inline int calc(int x, int cnt, int i){ if(!cnt) return times(x, phi[i]); if(i == totp) return c ? 1 : 0; return power(calc(x, cnt - 1, i + 1), i); } //同上 inline void prework(){ phi[0] = mod; while(phi[totp] > 1) totp++, phi[totp] = calc_phi(phi[totp - 1]); calc_c(); for(int i = 1; i <= n; ++i){ a[i][0] = read(); for(int j = 1; j <= totp + 1; ++j){ a[i][j] = calc(a[i][0], j, 0) % mod; } a[i][0] %= mod; } } } using namespace Prework; namespace Segment_Tree{ int sum[N << 2], mins[N << 2];//sum 記錄區間和,mins 記錄修改次數 inline void pushup(int rt){ mins[rt] = min(mins[ls], mins[rs]); sum[rt] = add(sum[ls] + sum[rs]); } inline void build(int l, int r, int rt){ if(l == r){ sum[rt] = a[l][0]; return; } int mid = (l + r) >> 1; build(l, mid, ls); build(mid + 1, r, rs); pushup(rt); } inline void update(int L, int R, int l, int r, int rt){ if(mins[rt] > totp) return; if(l == r) return sum[rt] = a[l][++mins[rt]], void(); int mid = (l + r) >> 1; if(L <= mid) update(L, R, l, mid, ls); if(R > mid) update(L, R, mid + 1, r ,rs); pushup(rt); } inline int query(int L, int R, int l, int r, int rt){ if(L <= l && r <= R) return sum[rt]; int mid = (l + r) >> 1; int res = 0; if(L <= mid) res = add(res + query(L, R, l, mid, ls)); if(R > mid) res = add(res + query(L, R, mid + 1, r, rs)); return res; } } using namespace Segment_Tree; signed main(){ n = read(), m = read(), mod = read(), c = read(); prework(); build(1, n, 1); while(m--){ int op = read(), l = read(), r = read(); if(!op) update(l, r, 1, n, 1); else write(query(l, r, 1, n, 1)), puts(""); } return 0; }
本文來自部落格園,作者:xixike,轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15719856.html