标记永久化

标记永久化是线段树的一个技巧,常用于无法(或难以)进行\(pushdown\)的较复杂的数据结构如主席树,树套树等。

如何做?对每个节点维护\(sum\)\(add\)

考虑修改,当询问与当前区间重合时,更新\(add+=val\),对所有经过的区间\(sum+=val\cdot (r-l+1)\)

void modify(int rt, int l, int r, int L, int R, int v)
{
	sum[rt]+=v*(R-L+1);
	if (l==L && r==R){add[rt]+=v; return;}
	if (R<=mid) modify(ls, l, mid, L, R, v);
	else if (L>mid) modify(rs, mid+1, r, L, R, v);
	else modify(ls, l, mid, L, mid, v),
		modify(rs, mid+1, r, mid+1, R, v);
}

考虑询问,累加经过的区间的\(add\),答案即为\(sum_{l,r}+\sum add\cdot (r-l+1)\)

int query(int rt, int l, int r, int L, int R, int Add)
{
	if (l==L && r==R) return sum[rt]+Add*(R-L+1);
	if (R<=mid) return query(ls, l, mid, L, R, Add+add[rt]);
	else if (L>mid) return query(rs, mid+1, r, L, R, Add+add[rt]);
	else return query(ls, l, mid, L, mid, Add+add[rt])
		+query(rs, mid+1, r, mid+1, R, Add+add[rt]);
}

一道模板

普通线段树区间加\(+\)区间求和,可以练练手。

#include<cstdio>
#define int long long
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=1000005;
int sum[N], add[N], a[N];

inline int read()
{
 	int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)

void build(int rt, int l, int r)
{
    if (l==r) {sum[rt]=a[l]; return;}
    build(ls, l, mid); build(rs, mid+1, r);
    sum[rt]=sum[ls]+sum[rs]; 
}

void modify(int rt, int l, int r, int L, int R, int v)
{
    sum[rt]+=v*(R-L+1);
    if (l==L && r==R){add[rt]+=v; return;}
    if (R<=mid) modify(ls, l, mid, L, R, v);
    else if (L>mid) modify(rs, mid+1, r, L, R, v);
    else modify(ls, l, mid, L, mid, v),
        modify(rs, mid+1, r, mid+1, R, v);
}

int query(int rt, int l, int r, int L, int R, int Add)
{
    if (l==L && r==R) return sum[rt]+Add*(R-L+1);
    if (R<=mid) return query(ls, l, mid, L, R, Add+add[rt]);
    else if (L>mid) return query(rs, mid+1, r, L, R, Add+add[rt]);
    else return query(ls, l, mid, L, mid, Add+add[rt])
        +query(rs, mid+1, r, mid+1, R, Add+add[rt]);
}

#undef mid
#undef ls
#undef rs

signed main()
{
    int n=read(), m=read();
    rep(i, 1, n) a[i]=read();
    build(1, 1, n);
    rep(i, 1, m)
    {
        int opt=read(), x=read(), y=read();
        if (opt==1) {int k=read(); modify(1, 1, n, x, y, k);}
        if (opt==2) printf("%lld\n", query(1, 1, n, x, y, 0));
    }
    return 0;
}

一道应用

也算是主席树上标记永久化的模板吧。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int rt[N], ls[N<<5], rs[N<<5], add[N<<5], tot, tim;
long long sum[N<<5];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

int build(int rt, int l, int r)
{
    rt=++tot; if (l==r) {scanf("%lld", &sum[rt]); return rt;}
    int mid=(l+r)>>1;
    ls[rt]=build(rt, l, mid); rs[rt]=build(rt, mid+1, r);
    sum[rt]=sum[ls[rt]]+sum[rs[rt]];
    return rt;
}

int update(int oldrt, int L, int R, int l, int r, int x)
{
    int rt=++tot;
    ls[rt]=ls[oldrt]; rs[rt]=rs[oldrt];
    add[rt]=add[oldrt]; sum[rt]=sum[oldrt];
    sum[rt]+=1ll*(min(r, R)-max(l, L)+1)*x;
    if (l>=L && r<=R) {add[rt]+=x; return rt;}
    int mid=(l+r)>>1;
    if (L<=mid) ls[rt]=update(ls[rt], L, R, l, mid, x);
    if (R>mid) rs[rt]=update(rs[rt], L, R, mid+1, r, x);
    return rt;
}

long long query(int rt, int L, int R, int l, int r)
{
    if (l>=L && r<=R) return sum[rt];
    long long res=1ll*(min(r, R)-max(l, L)+1)*add[rt];
    int mid=(l+r)>>1;
    if (L<=mid) res+=query(ls[rt], L, R, l, mid);
    if (R>mid) res+=query(rs[rt], L, R, mid+1, r);
    return res;
}

void clear()
{
    tot=tim=0;
    memset(sum, 0, sizeof(sum));
    memset(rt, 0, sizeof(rt));
    memset(add, 0, sizeof(add));
}

int main()
{
    int n, m;
    while (~scanf("%d%d", &n, &m))
    {
        clear(); rt[0]=build(0, 1, n);
        while (m--)
        {
            char opt; scanf(" %c", &opt);
            if (opt=='C') 
            {
                int l=read(), r=read(), d=read();
                rt[++tim]=update(rt[tim-1], l, r, 1, n, d);
            }
            if (opt=='Q')
            {
                int l=read(), r=read();
                printf("%lld\n", query(rt[tim], l, r, 1, n));
            }
            if (opt=='H')
            {
                int l=read(), r=read(), t=read();
                printf("%lld\n", query(rt[t], l, r, 1, n));
            }
            if (opt=='B') tim=read();
        }
    }
    return 0;
}
posted @ 2019-04-29 21:18  OIerC  阅读(256)  评论(0编辑  收藏  举报