hdu5828 Rikka with Sequence (线段树:区间开根+区间求和+区间加减)
来源:互联网 发布:linux下启动mysql 编辑:程序博客网 时间:2024/05/17 08:39
题意:
对一个n元素序列进行三种操作:1 l r x : 对a[l,r]之间所有元素都加上x2 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,rll 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;}
阅读全文
0 0
- hdu5828 Rikka with Sequence (线段树:区间开根+区间求和+区间加减)
- HDU 5828-H - Rikka with Sequence-线段树+玄学-区间开方/区间更新/区间求和
- HDU5828 区间开根加法求和 线段树
- 线段树,区间开方(Rikka with Sequence,HDU 5828)
- hdu5828 Rikka with Sequence(线段树)
- POJ 3468 A Simple Problem with Integers(线段树功能:区间加减区间求和)
- POJ 3468 A Simple Problem with Integers(线段树|区间加减&&区间求和)
- A Simple Problem with Integers 【线段树】-区间加减求和
- 【HDU5828】Rikka with Sequence(线段树)
- POJ3468——A Simple Problem with Integers(线段树区间求和加减)
- 【线段树】区间求和+区间修改(区间加)
- 线段树区间求和
- 线段树模板 区间加减 区间修改
- POJ 3468(线段树,区间加减 询问区间和)
- poj 3468 线段树对一个区间加减和求和
- A Simple Problem with Integers(线段树,区间求和)
- HDU 5634-Rikka with Phi(线段树区间更新)
- 线段树(区间修改,区间求和)
- GIT远程仓库回退方法
- 字符串
- window.location获取url各项参数详解
- git学习(一)
- Ceres(2)---Analytic,Numeric, Automatic derivatives
- hdu5828 Rikka with Sequence (线段树:区间开根+区间求和+区间加减)
- Executor框架
- 最简单的Java List的 分组(多字段)排序
- 架构师
- kotlin dataBinding 不支持
- 关闭主窗口显示确认退出窗口
- React Native 轻松集成分享功能(iOS 篇)
- js获取当前时间(yyyy-mm-dd hh:mm:ss)的方法
- dip与px相互转换工具类