HDU - 4348To the moon
【题目描述】
【题目分析】
题目中说明每次更新后时间都会加1,而且还会需要查询以前的区间,还会需要返回以前的时间,所以是很裸的主席树。区间查询的话我们同样需要用到lazy标记
通过这道题我发现线段树的操作还是很灵活的
借鉴大佬的代码
【AC代码】
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;typedef long long ll;const int MAXN=100005;
int n,m,Time;
ll a[MAXN];
struct node
{int ls,rs;ll sum,add;
}tree[MAXN*40];
int root[MAXN*40];
int tot;
void build(int &o,int l,int r)
{o=++tot;tree[o].add=0;if(l==r){tree[o].sum=a[l];return;}int mid=(l+r)>>1;build(tree[o].ls,l,mid);build(tree[o].rs,mid+1,r);tree[o].sum=tree[tree[o].ls].sum+tree[tree[o].rs].sum;
}void interval_add(int &x,int l,int r,int L,int R,ll z)
{tree[++tot]=tree[x]; x=tot;tree[x].sum+=z*(R-L+1); //相当于pushupif(l==L && r==R){tree[x].add+=z;return;}int mid=(l+r)>>1;if(R<=mid) interval_add(tree[x].ls,l,mid,L,R,z);//这里通过判断让L,R属于l..mid,方便上面的pushupelse if(L>mid) interval_add(tree[x].rs,mid+1,r,L,R,z);else{interval_add(tree[x].ls,l,mid,L,mid,z);interval_add(tree[x].rs,mid+1,r,mid+1,R,z);}
}ll query(int o,int l,int r,int L,int R)
{if(l>=L && r<=R){return tree[o].sum;}ll ret=tree[o].add*(R-L+1); //pushdown,这里的pushdown没有将标记下传,而是将lazy标记保留,询问的时候依次将路径上的lazy标记加起来int mid=(l+r)>>1;if(R<=mid) ret+=query(tree[o].ls,l,mid,L,R);else if(L>mid) ret+=query(tree[o].rs,mid+1,r,L,R);else{ret+=query(tree[o].ls,l,mid,L,mid);ret+=query(tree[o].rs,mid+1,r,mid+1,R);}return ret;
}int main()
{char cmd[5];int l,r,t;ll d;while(~scanf("%d%d",&n,&m)){for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}tot=0; Time=0;build(root[0],1,n);for(int i=0;i<m;i++){scanf("%s",cmd);if(cmd[0]=='C'){scanf("%d%d%lld",&l,&r,&d);Time++;root[Time]=root[Time-1];interval_add(root[Time],1,n,l,r,d);}else if(cmd[0]=='Q'){scanf("%d%d",&l,&r);printf("%lld\n",query(root[Time],1,n,l,r));}else if(cmd[0]=='H'){scanf("%d%d%d",&l,&r,&t);printf("%lld\n",query(root[t],1,n,l,r));}else if(cmd[0]=='B'){scanf("%d",&t);Time=t;}}}return 0;
}