1. 程式人生 > >題解【luogu4145 上帝造題的七分鐘2(花神遊歷各國)】

題解【luogu4145 上帝造題的七分鐘2(花神遊歷各國)】

題目大意

一個序列,支援區間開方與求和操作。

演算法:線段樹實現開方修改與區間求和

分析

  • 顯然,這道題的求和操作可以用線段樹來維護
  • 但是如何來實現區間開方呢
  • 大家有沒有這樣的經歷:玩計算器的時候,把一個數瘋狂的按開方,最後總會變成 \(1\),之後在怎樣開方也是 \(1\) (\(\sqrt1=1\))
  • 同樣的,\(\sqrt0=0\)
  • 所以,只要一段區間裡的所有數全都 \(\leq 1\) 了,便可以不去修改它

實現

  • 線段樹維護區間和 \(sum\) 與最大值 \(Max\)
  • 在修改過程中,只去修改 \(Max > 1\) 的區間
  • 到了葉子節點對\(sum\)
    \(Max\)進行開方就行了

複雜度

  • 每個數 \(\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; //防抄
}