題解 P5610 【[Ynoi2013]大學】
阿新 • • 發佈:2020-10-12
題意簡述
區間查 \(x\) 的倍數併除掉,區間查和。
題解
平衡樹。
首先有個基本的想法就是按 \(a_{i}\) 開平衡樹,即對於每個 \(a_{i}\) 都開一棵平衡樹,共5e5棵,第 \(i\) 棵平衡樹維護的值是所有 \(a_{i}\) 的倍數在原陣列中的下標,用處後面講。
注意到一個小性質,一個正整數 \(A\) 最多被整除 \(\log_{2}A\) 次,這個很好想,每次都至少減少一半。可以當成一個 trick 記下來。
整個區間的數最多被除 \(\sum_{i=1}^{n}\log_{2}a_{i}\) 次,區間和的操作可以用樹狀陣列操作實現,則整體的操作複雜度為 \(\Theta(\sum_{i=1}^{n}\log_{2}a_{i}+\log_{2}a_{i})\)
開頭提到了對於每個 \(a_{i}\) 我們都開一棵平衡樹,作用就體現在這裡。因為如果要保證正確的時間複雜度,我們需要快速的找到需要執行操作的數。
這裡我採用的是 FHQ-Treap。
我們可以用兩次 split
操作在 \(x\) 的平衡樹中提取出當前的詢問區間,由於我們是以下標為平衡樹維護的權值,所以我們用按值分裂即可提取出區間。
然後我們就在提取出的子樹中 DFS 遍歷,然後暴力操作,把操作後依然是 \(x\) 的倍數的數記錄下來,操作完後用這個陣列再建一棵臨時樹然後和之前 split
出來的子樹一起合併回去。
操作之前記得預處理每個數的所有約數,這個簡單,直接用 vector 即可。
#include <cstdio> #include <cstdlib> #include <algorithm> #include <ctime> #include <queue> using namespace std; typedef long long t_t; const int Maxn = 1e5 + 5; const int Maxa = 5e5 + 5; int n, m, xxx, zip, tot, isa[Maxn], bin[Maxa], root[Maxa]; struct Treap { int l, r, key, val; } t[Maxa * 230]; struct Fenwick_Tree { t_t bit[Maxn]; void ins(int x, t_t v) { for (; x <= n; x += x & -x) bit[x] += v; } t_t sum(int x) { t_t ret = 0; for (; x; x -= x & -x) ret += bit[x]; return ret; } } fwt; vector < int > vec[Maxa]; int newnode(int val) { t[++tot].val = val; t[tot].key = rand(); return tot; } void split(int now, int val, int &x, int &y) { if (now == 0) x = y = 0; else { if (t[now].val <= val) { x = now; split(t[now].r, val, t[now].r, y); } else { y = now; split(t[now].l, val, x, t[now].l); } } } int merge(int x, int y) { if (x == 0 || y == 0) return x | y; if (t[x].key > t[y].key) { t[x].r = merge(t[x].r, y); return x; } else { t[y].l = merge(x, t[y].l); return y; } } int build(int l, int r) { if (l > r) return 0; int mid = (l + r) >> 1; int now = newnode(bin[mid]); t[now].l = build(l, mid - 1); t[now].r = build(mid + 1, r); return now; } void dfs(int now, int val) { if (now == 0) return ; dfs(t[now].l, val); if (isa[t[now].val] % val == 0) { fwt.ins(t[now].val, -isa[t[now].val]); isa[t[now].val] /= val; fwt.ins(t[now].val, isa[t[now].val]); } if (isa[t[now].val] % val == 0) bin[++zip] = t[now].val; dfs(t[now].r, val); } int tx, ty, tp; void change(int l, int r, int x) { if (x == 1) return ; split(root[x], r, tx, tp); split(tx, l - 1, tx, ty); zip = 0; dfs(ty, x); root[x] = merge(tx, merge(build(1, zip), tp)); } signed main() { srand((114514 - 1) / 3 - 4); scanf("%d %d", &n, &m); for (int i = 1; i <= n; ++i) { scanf("%d", &isa[i]); fwt.ins(i, isa[i]); xxx = max(xxx, isa[i]); } for (int i = 1; i <= n; ++i) { for (int j = 1; j * j <= isa[i]; ++j) { if (isa[i] % j == 0) { vec[j].push_back(i); if (j * j != isa[i]) vec[isa[i] / j].push_back(i); } } } for (int i = 1; i <= xxx; ++i) { zip = 0; for (unsigned j = 0; j < vec[i].size(); ++j) bin[++zip] = vec[i][j]; root[i] = build(1, zip); } for (int i = 0; i < m; ++i) { int t, l, r, x; scanf("%d %d %d", &t, &l, &r); if (t == 1) { scanf("%d", &x); change(l, r, x); } else printf("%lld\n", fwt.sum(r) - fwt.sum(l - 1)); } return 0; }