題解【luogu4145 上帝造題的七分鐘2(花神遊歷各國)】
阿新 • • 發佈:2018-12-03
題目大意:
一個序列,支援區間開方與求和操作。
演算法:線段樹實現開方修改與區間求和
分析:
- 顯然,這道題的求和操作可以用線段樹來維護
- 但是如何來實現區間開方呢
- 大家有沒有這樣的經歷:玩計算器的時候,把一個數瘋狂的按開方,最後總會變成 \(1\),之後在怎樣開方也是 \(1\) (\(\sqrt1=1\))
- 同樣的,\(\sqrt0=0\)
- 所以,只要一段區間裡的所有數全都 \(\leq 1\) 了,便可以不去修改它
實現:
- 線段樹維護區間和 \(sum\) 與最大值 \(Max\)
- 在修改過程中,只去修改 \(Max > 1\) 的區間
- 到了葉子節點對\(sum\)
複雜度:
- 每個數 \(\leq 10 ^ {12}\),所以至多開方\(6\)次便可以得到\(1\)
- 每次操作是 \(\log n\)的,總複雜度\(O(n \log n)\)
注意事項:
- 請使用long long
- 可能 \(l > r\)
(把我坑了)
程式碼:
#include <iostream> #include <cstdlib> #include <cmath> #include <cstdio> using namespace std; typedef long long LL; const int MAXN = 100100; int n, m; int cnt; LL a[MAXN]; struct node { int left, right; LL s, Max; node *ch[2]; }pool[MAXN << 2], *root; inline void pushup(node *r) { r->s = r->ch[0]->s + r->ch[1]->s; r->Max = max(r->ch[0]->Max, r->ch[1]->Max); } inline void Build_Tree(node *r, int left, int right) { r->left = left; r->right = right; if(left == right) { r->s = r->Max = a[left]; return ; } int mid = (left + right) / 2; node *lson = &pool[++cnt]; node *rson = &pool[++cnt]; r->ch[0] = lson; r->ch[1] = rson; Build_Tree(lson, left, mid); Build_Tree(rson, mid + 1, right); pushup(r); } inline void change(node *r, int left, int right) { if(r->left == r->right) { r->s = sqrt(r->s); r->Max = sqrt(r->Max); return ; } int mid = (r->left +r-> right) / 2; if(left <= mid && r->ch[0]->Max > 1) change(r->ch[0], left, right); if(mid < right && r->ch[1]->Max > 1) change(r->ch[1], left, right); pushup(r); } inline LL query(node *r, int left, int right) { if(r->left == left && r->right == right) return r->s; if(r->ch[0]->right >= right) return query(r->ch[0], left, right); else if(r->ch[1]->left <= left) return query(r->ch[1], left, right); else return query(r->ch[0], left, r->ch[0]->right) + query(r->ch[1], r->ch[1]->left, right); } int main() { scanf("%d", &n); root = &pool[0]; for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); scanf("%d", &m); Build_Tree(root, 1, n); for(int i = 1; i <= m; i++) { int opt, l, r; scanf("%d%d%d", &opt, &l, &r); if(l > r) swap(l, r); if(opt) printf("%lld\n", query(root, l, r)); else change(root, l, r); } return 1; //防抄 }