• 标记永久化


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

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

    考虑修改,当询问与当前区间重合时,更新(add+=val),对所有经过的区间(sum+=valcdot (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 addcdot (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
    ", 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
    ", query(rt[tim], l, r, 1, n));
                }
                if (opt=='H')
                {
                    int l=read(), r=read(), t=read();
                    printf("%lld
    ", query(rt[t], l, r, 1, n));
                }
                if (opt=='B') tim=read();
            }
        }
        return 0;
    }
    
  • 相关阅读:
    mac下copy paste
    【leetcode】151. 颠倒字符串中的单词
    【leetcode】42. 接雨水
    【leetcode】239. 滑动窗口最大值
    【leetcode】 15. 三数之和
    【leetcode】18. 四数之和
    go env设置环境变量不起作用
    WSL2的迁移、默认设置、ssh、固定IP
    Shell的环境变量
    MOS, CMOS, 双向开关, PAD
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10792961.html
Copyright © 2020-2023  润新知