bzoj 3211 花神遊歷各國 線段樹區間開方
阿新 • • 發佈:2022-02-13
題目大意
給定一個序列,支援區間開方、區間求和
\(n \leq 10^5,a_i \leq 10^9\)
思路
由於\(a_i \leq 10^9\),因此每一個元素至多開方五次就會變為1,因此每個點至多會被修改5次,複雜度\(O(nlogn)\)
程式碼
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[N]; #define ll long long struct node { int l,r; ll sum; ll set; }t[N << 2]; int read () { int q = 0,f = 1; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-')f = -1; ch = getchar(); } while(isdigit(ch)) { q = q * 10 + ch - '0'; ch = getchar(); }return q * f; } void update(int rt) { int ch = rt << 1; t[rt].sum = t[ch].sum + t[ch + 1].sum; t[rt].set = (t[ch].set && t[ch + 1].set); } void build(int l,int r,int rt) { t[rt].l = l; t[rt].r = r; if (l == r) { t[rt].sum = a[l]; t[rt].set = (a[l] <= 1); return; } int mid = (l + r) >> 1; int ch = rt << 1; build(l,mid,ch); build(mid + 1,r,ch + 1); update(rt); } void modify(int l,int r,int rt) { if(t[rt].set) { //優化,如果當前不需要開方就不開了 return; } if(t[rt].l == t[rt].r) { t[rt].sum = (ll)sqrt(t[rt].sum + 0.5); t[rt].set = t[rt].sum <= 1; } else { int mid = (t[rt].l + t[rt].r) >> 1; int ch = rt << 1; if(r <= mid) { modify(l,r,ch); }else if(l > mid) { modify(l,r,ch + 1); }else { modify(l,mid,ch); modify(mid + 1,r,ch + 1); } update(rt); } } ll query(int l,int r,int rt) { //cout << l << ' ' << r << endl; if(l <= t[rt].l and t[rt].r <= r) return t[rt].sum; int mid = (t[rt].l + t[rt].r) >> 1; ll res = 0; int ch = rt << 1; if(l <= mid) { res += query(l,r,ch); } if(r > mid) { res += query(l,r,ch + 1); } return res; } int n,m; int main () { n = read(); for(int i = 1;i <= n; i ++) a[i] = read(); build(1,n,1); m = read(); while(m --) { int x,l,r; x = read(),l = read(),r = read(); if(x == 1) { printf("%lld\n",query(l,r,1)); } else { modify(l,r,1); } } return 0; }