标记永久化是线段树的一个技巧,常用于无法(或难以)进行(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;
}