题目链接1(内存限制64MB,时间限制2s)
题目链接2(内存限制1536MB,时间限制1s)
上面两个题目一样,但是条件限制不同。
题意:给一串数字。有四种不同的操作。
- Q L R 查询当前[L,R]的数字之和
- H L R T 查询在T时[L,R]的数字之和
- C L R X 使[L,R]所有的数字加上X,并且+1s
- B T 回到T时刻
解题思路:
如果我们用可持续化线段树去完成这道题。最大的难题是如何保证后来生成的树不会对以前的树产生影响。MOGU目前只知道两种方法。
如果是因为三操作生成的树,如果会对前面的已经存在的旧树产生影响,那么就是pushdown操作。所以我们的思考重点可以放在pushdown上面。
方法一:判断pushdown是,该节点的子节点是否是完全新建的,还是在老树上的。如果是在老树上,那么就新建一个。该方法容易想到,但是对内存的消耗很大。上面的链接只能过掉题目链接2.
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=1E6+5;
int rt[maxn<<4];
int root_num;
struct node{
LL val,lazy;
int ls,rs;
int mark;
}tree[maxn<<4];
void push_up(int root){
tree[root].val=tree[tree[root].ls].val+tree[tree[root].rs].val;
}
int build_tree(int l,int r){
int t=++root_num;
tree[t].val=tree[t].mark=tree[t].lazy=0;
if (l==r){
scanf("%lld",&tree[t].val);
tree[t].ls=tree[t].rs=0;
return t;
}
int mid=(l+r)>>1;
tree[t].ls=build_tree(l,mid);
tree[t].rs=build_tree(mid+1,r);
push_up(t);
return t;
}
void push_down(int l,int r,int root){
if (tree[root].mark){
int ls=++root_num;
int rs=++root_num;
tree[ls]=tree[tree[root].ls];
tree[rs]=tree[tree[root].rs];
tree[root].ls=ls,tree[root].rs=rs;
tree[ls].mark=tree[rs].mark=1;
tree[root].mark=0;
}
if (tree[root].lazy) {
int ls=tree[root].ls,rs=tree[root].rs;
int mid=(l+r)>>1;
tree[ls].val+=tree[root].lazy*(mid-l+1);
tree[rs].val+=tree[root].lazy*(r-mid);
tree[rs].lazy+=tree[root].lazy;
tree[ls].lazy+=tree[root].lazy;
tree[root].lazy=0;
}
}
int update(int l,int r,int ql,int qr,int root,int val){
int t=++root_num;
if (l==ql&&r==qr){
tree[t]=tree[root];
tree[t].val+=val*(r-l+1);
tree[t].lazy+=val;
tree[t].mark=l==r?0:1;
return t;
}
int mid=(l+r)>>1;
tree[t].mark=tree[t].val=tree[t].lazy=0;
push_down(l,r,root);
if (qr<=mid) {
tree[t].ls=update(l,mid,ql,qr,tree[root].ls,val);
tree[t].rs=tree[root].rs;
}
else if (ql>mid) {
tree[t].ls=tree[root].ls;
tree[t].rs=update(mid+1,r,ql,qr,tree[root].rs,val);
}
else {
tree[t].ls=update(l,mid,ql,mid,tree[root].ls,val);
tree[t].rs=update(mid+1,r,mid+1,qr,tree[root].rs,val);
}
push_up(t);
return t;
}
LL query(int l,int r,int ql,int qr,int root){
if (l==ql&&r==qr){
return tree[root].val;
}
int mid=(l+r)>>1;
push_down(l,r,root);
if (qr<=mid) return query(l,mid,ql,qr,tree[root].ls);
else if (ql>mid) return query(mid+1,r,ql,qr,tree[root].rs);
else return query(l,mid,ql,mid,tree[root].ls)+query(mid+1,r,mid+1,qr,tree[root].rs);
}
int main (){
int n,m;
while (~scanf("%d%d",&n,&m)){
int cnt=0;
root_num=0;
rt[cnt]=build_tree(1,n);
for (int i=1;i<=m;i++){
char str[10];
int l,r,x;
scanf("%s",str);
if (str[0]=='C'){
scanf("%d%d%d",&l,&r,&x);
cnt++;
rt[cnt]=update(1,n,l,r,rt[cnt-1],x);
}
if (str[0]=='Q'){
scanf("%d%d",&l,&r);
printf("%lld\n",query(1,n,l,r,rt[cnt]));
}
if (str[0]=='H'){
scanf("%d%d%d",&l,&r,&x);
printf("%lld\n",query(1,n,l,r,rt[x]));
}
if (str[0]=='B'){
scanf("%d",&x);
cnt=x;
}
}
puts("");
}
return 0;
}
方法二:可以不用pushdown操作,那么我们就可以纪录每次 从根节点到我们要求的区间所遇见的所有lazy。因为我们从根节点到要查询的区间,碰到的每一个区间都是我们要求的区间的父区间。换而言之,我们不用pushdown操作,把lazy推下去,但是我们可以在二分查找目标区间时,纪录碰到的lazy。最后起到的效果是一样的。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define LL long long
#define MT(a,b) memset(a,b,sizeof(a))
const int mod=10007;
const int maxn=1e5+5;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
LL root_num;
int rt[maxn];
struct node{
LL val,lazy;
int ls,rs;
}tree[maxn<<5];
void push_up(int root){tree[root].val=tree[tree[root].ls].val+tree[tree[root].rs].val;}
int build_tree(int l,int r){
int t=++root_num;
tree[t].val=tree[t].lazy=0;
if (l==r){
scanf("%lld",&tree[t]);
tree[t].ls=tree[t].rs=0;
return t;
}
int mid=(l+r)>>1;
tree[t].ls=build_tree(l,mid);
tree[t].rs=build_tree(mid+1,r);
push_up(t);
return t;
}
int update(int l,int r,int ql,int qr,int val,int root,LL res){
int t=++root_num;
tree[t]=tree[root];
if (l==ql&&r==qr){
tree[t].lazy+=val;
return t;
}
tree[t].val += val*(qr-ql+1);
int mid=(l+r)>>1;
res+=tree[root].lazy;
if (qr<=mid) {
tree[t].ls=update(l,mid,ql,qr,val,tree[root].ls,res);
tree[t].rs=tree[root].rs;
}
else if (ql>mid) {
tree[t].ls=tree[root].ls;
tree[t].rs=update(mid+1,r,ql,qr,val,tree[root].rs,res);
}
else{
tree[t].ls=update(l,mid,ql,mid,val,tree[root].ls,res);
tree[t].rs=update(mid+1,r,mid+1,qr,val,tree[root].rs,res);
}
return t;
}
LL query(int l,int r,int ql,int qr,int root,LL res){
res+=tree[root].lazy;
if (l==ql&&r==qr){
return tree[root].val+res*(r-l+1);
}
int mid=(l+r)>>1;
if (qr<=mid) return query(l,mid,ql,qr,tree[root].ls,res);
else if (ql>mid ) return query(mid+1,r,ql,qr,tree[root].rs,res);
else return query(l,mid,ql,mid,tree[root].ls,res)+query(mid+1,r,mid+1,qr,tree[root].rs,res);
}
int main (){
LL n,m;
while (~scanf("%lld%lld",&n,&m)){
int cnt=0;
root_num=0;
rt[cnt]=build_tree(1,n);
char str[5];
while (m--){
int st,ed,x;
scanf("%s",str);
if (str[0]=='C'){
scanf("%d%d%d",&st,&ed,&x);
cnt++;
rt[cnt]=update(1,n,st,ed,x,rt[cnt-1],0);
}
if (str[0]=='Q'){
scanf("%d%d",&st,&ed);
printf("%lld\n",query(1,n,st,ed,rt[cnt],0));
}
if (str[0]=='H'){
scanf("%d%d%d",&st,&ed,&x);
printf("%lld\n",query(1,n,st,ed,rt[x],0));
}
if (str[0]=='B'){
scanf("%d",&x);cnt=x;
}
}
}
return 0;
}