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,那就递归暴力访问更新。直至变成上面的情况。

注意点:

  1. laz标记用来标记“区间加减”的时候,初始值一定是 0 而不是 -1,-1 的话在第一次laz[rt] += x 时会出错。
  2. 试了一次输入外挂, 发现确实会快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
原创粉丝点击