hdu5828 Rikka with Sequence (线段树:区间开根+区间求和+区间加减)
2017-08-04 11:33
435 查看
题意:
对一个n元素序列进行三种操作: 1 l r x : 对a[l,r]之间所有元素都加上x 2 l r: 对a[l,r]之间所有元素开根号 3 l r: 输出a[l,r]之间的元素和 (1<=n,m<=1e5 , 1<=A[i],x<=1e5)
分析:
这道题难点就在“区间开根”这一操作。因为如果像正常的区间更新一样,用懒惰标记,我们会发现无法快速更新区间信息,也就是说无法快速得到它询问的区间和。所以,类似“区间取模”,需要分析开根操作的特点。我们发现,即使是两个差很大的数,都可以在很少次数的开根计算后趋近于相等,最后都变成1。
当一个区间内的数都相等会怎样呢?
这时区间开根操作就变为了区间减法,开根就相当于所有的数都减了一个数。
当一个区间的极差是1会怎样呢?开根后会有两种情况:
极差仍然为1。那开根就相当于都减去一个数。
极差变为0。下次开根就是区间减法。(此处也可看作是区间覆盖)
若区间极差大于1,那就递归暴力访问更新。直至变成上面的情况。
注意点:
laz标记用来标记“区间加减”的时候,初始值一定是 0 而不是 -1,-1 的话在第一次laz[rt] += x 时会出错。试了一次输入外挂, 发现确实会快300ms
代码:
#include <iostream> #include <algorithm> #include <queue> #include <stack> #include <vector> #include <set> #include <cmath> #include <cstdlib> #include <cstring> #include <cstdio> using namespace std; #define ms(a,b) memset(a,b,sizeof(a)) typedef long long ll; const int MAXN = 1e5 + 5; const double EPS = 1e-8; const int INF = 0x3f3f3f3f; #define lson rt*2,l,(l+r)/2 #define rson rt*2+1,(l+r)/2+1,r ll mx[MAXN << 2], mi[MAXN << 2], sum[MAXN << 2], laz[MAXN << 2]; int n; void pushup(int rt) { sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]); mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]); } void pushdown(int rt, int l, int r) { if (laz[rt] != 0) { int mid = (l + r) / 2; laz[rt << 1] += laz[rt]; laz[rt << 1 | 1] += laz[rt]; sum[rt << 1] += laz[rt] * (mid - l + 1); sum[rt << 1 | 1] += laz[rt] * (r - mid); mx[rt << 1] += laz[rt]; mx[rt << 1 | 1] += laz[rt]; mi[rt << 1] += laz[rt]; mi[rt << 1 | 1] += laz[rt]; laz[rt] = 0; } return; } void build(int rt, int l, int r) { if (l == r) { scanf("%lld", &sum[rt]); mx[rt] = mi[rt] = sum[rt]; return; } build(lson); build(rson); pushup(rt); } void update(int L,int R,int rt, int l, int r, ll x) { if (L <= l && R >= r) { laz[rt] += x; sum[rt] += x * (r - l + 1); mx[rt] += x; mi[rt] += x; return; } pushdown(rt, l, r); if (L <= (l + r) / 2) update(L,R,lson, x); if (R > (l + r) / 2) update(L,R,rson, x); pushup(rt); } void sub(int rt, int l, int r, ll x) { laz[rt] -= x; sum[rt] -= (r - l + 1) * x; mx[rt] -= x; mi[rt] -= x; } void change(int L,int R,int rt, int l, int r) { if (L <= l && R >= r) { ll a = mx[rt], b = mi[rt]; if ((a - b) <= 1 && ((ll)sqrt(a) - (ll)sqrt(b)) == (a - b)) { sub(rt, l, r, a - (ll)sqrt(a)); return; } } pushdown(rt, l, r); if (L <= (l + r) / 2) change(L,R,lson); if (R > (l + r) / 2) change(L,R,rson); pushup(rt); } ll query(int L,int R,int rt, int l, int r) { if (L <= l && R >= r) { return sum[rt]; } pushdown(rt, l, r); ll ans = 0; if (L <= (l + r) / 2) ans += query(L,R,lson); if (R > (l + r) / 2) ans += query(L,R,rson); pushup(rt); return ans; } int main() { int T, m; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); build(1, 1, n); ms(laz, 0); while (m--) { int op,L,R; ll x; scanf("%d%d%d", &op, &L, &R); if (op == 1) {scanf("%lld", &x); update(L,R,1, 1, n, x);} else if (op == 2) change(L,R,1, 1, n); else printf("%lld\n", query(L,R,1, 1, n)); } } return 0; }
相关文章推荐
- Rikka with Sequence---hdu5828(区间更新与查找 线段树)
- A Simple Problem with Integers 【线段树】-区间加减求和
- HDU 5828-H - Rikka with Sequence-线段树+玄学-区间开方/区间更新/区间求和
- POJ 3468 A Simple Problem with Integers(线段树功能:区间加减区间求和)
- 【HDU5828】Rikka with Sequence(线段树)
- POJ 3468 A Simple Problem with Integers(线段树|区间加减&&区间求和)
- HDU5828 Rikka with Sequence 线段树
- poj 3468 A Simple Problem with Integers(线段树+区间更新+区间求和)
- poj 3468 A Simple Problem with Integers 线段树 区间更新求和
- POJ3468 A Simple Problem with Integers 线段树|树状数组BIT(区间增减,求和)
- POJ 3468 A Simple Problem with Integers (线段树 区间增减 区间求和)
- poj 3468 A Simple Problem with Integers(线段树)(第二部分 成段更新,区间求和)
- 线段树 区间求和 poj 3468 A Simple Problem with Integers
- HDU5828-Rikka with Sequence
- POJ3468 A Simple Problem with Integers(线段树成段增减,区间求和)
- 【线段树区间求和裸题】poj 3468 A Simple Problem with Integers
- HDU 5828 Rikka with Sequence (线段树)
- poj 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)
- HDU-5828-Rikka with Sequence(线段树)
- HDU 5828 Rikka with Sequence 线段树优化