HDU 4348 To the moon 主席树 + 区间更新

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4348

题意:给定长度为n的数组,有四种操作:C l r d把[l, r]内的元素全部加上d并把时间计数器加1,Q l r查询[l, r]内元素的和,H l r t查询t时刻[l, r]内元素的和,B t返回时刻t(保证只向前而不向后跳)

思路:跳回之前的某个状态,明显是可持久化数据结构,这题就是明显的主席树。此题中区间更新必须lazy操作,不然新建的节点太多必定内存爆炸。如果像普通线段树那样lazy操作还是会内存爆炸,因此要换一种方式:我们用一个标记记录当前区间的累加和,查询时从根节点走到目标节点的过程中不断累加所经过节点的标记值

总结:主席树区间更新和普通线段树区间更新还是不一样的,如果像普通线段树那样,每次需要新建(r - l + 1)log(n)个节点,肯定不行,只能另取它法

#include 
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int N = 200010;
int root[N], lson[N*20], rson[N*20];
ll sum[N*20], mark[N*20];
int a[N];
int n, m, tot;
void push_up(int rt)
{
    sum[rt] = sum[lson[rt]] + sum[rson[rt]];
}
void build(int l, int r, int &rt) //建树
{
    rt = ++tot;
    sum[rt] = mark[rt] = 0;
    if(l == r)
    {
        sum[rt] = a[l]; return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, lson[rt]);
    build(mid + 1, r, rson[rt]);
    push_up(rt);
}
void update(int pre, int &rt, int x, int y, int l, int r, int c)
{
    rt = ++tot;
    lson[rt] = lson[pre], rson[rt] = rson[pre], mark[rt] = mark[pre];
    sum[rt] = sum[pre] + (ll)(y - x + 1) * c; //所管辖区间的和
    if(l == x && r == y) //记录此区间被累加的和
    {
        mark[rt] += c; return;
    }
    int mid = (l + r) >> 1;
    if(y <= mid) update(lson[pre], lson[rt], x, y, l, mid, c);
    else if(x > mid) update(rson[pre], rson[rt], x, y, mid + 1, r, c);
    else
    {
        update(lson[pre], lson[rt], x, mid, l, mid, c);
        update(rson[pre], rson[rt], mid + 1, y, mid + 1, r, c);
    }
}
ll query(int x, int y, int l, int r, int rt)
{
    ll ans = (ll)(y - x + 1) * mark[rt]; //不断累加所经过节点的标记值
    if(l == x && y == r) return sum[rt];
    int mid = (l + r) >> 1;
    if(y <= mid) ans += query(x, y, l, mid, lson[rt]);
    else if(x > mid) ans += query(x, y, mid + 1, r, rson[rt]);
    else
    {
        ans += query(x, mid, l, mid, lson[rt]);
        ans += query(mid + 1, y, mid + 1, r, rson[rt]);
    }
    return ans;
}
int main()
{
    while(~ scanf("%d%d", &n, &m))
    {
        tot = 0;
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        build(1, n, root[0]);
        char ch;
        int v, u, c, cnt = 0;
        for(int i = 1; i <= m; i++)
        {
            scanf(" %c%d", &ch, &v);
            if(ch == 'C')
            {
                scanf("%d%d", &u, &c);
                update(root[cnt], root[cnt+1], v, u, 1, n, c);
                cnt++;
            }
            else if(ch == 'Q')
            {
                scanf("%d", &u);
                printf("%I64d\n", query(v, u, 1, n, root[cnt]));
            }
            else if(ch == 'H')
            {
                scanf("%d%d", &u, &c);
                printf("%I64d\n", query(v, u, 1, n, root[c]));
            }
            else if(ch == 'B')
            {
                cnt = v;
                tot = root[cnt+1];
            }
        }
    }
    return 0;
}


你可能感兴趣的:(主席树)