题意:一段挂钩分成n段,每一段可以由铜、银、金做成,对应的价值分别是1,2,3,现在有m个操作(a,b,x)意思是改变区间[a,b]段的钩子为材质x,问m个操作之后这段挂钩的价值总和。挂钩的每一段初始为铜,也就是1。
分析:
区间更新,单点查询。确切的说是区间替换,询问总区间总和。线段树的典型应用之一。因为是区间更新,所以用 lazy[rt] 数组记录当前子树是否曾经更新过,lazy[rt]!=-1表示在本操作之前就已经替换过rt代表的子区间的值,那么需要把上次的更新操作向下执行完。详见注释。
代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100000; int t,n,q; int sum[maxn*4+10]; int lazy[maxn*4+10]; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void creat(int l,int r,int rt) { lazy[rt]=-1; if(l==r){ //l==r表示当前区间是元区间也就是一个点,在本题中表示某一段 sum[rt]=1; //根为rt的区间的总和 return; } int mid=(l+r)>>1; creat(l,mid,rt<<1); creat(mid+1,r,rt<<1|1); pushup(rt); //建好子树之后向上求区间和 } void pushdown(int d,int rt) { if(lazy[rt]!=-1){ lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt]; sum[rt<<1]=(d-(d>>1))*lazy[rt]; sum[rt<<1|1]=(d>>1)*lazy[rt]; lazy[rt]=-1; } } void upd(int a,int b,int x,int l,int r,int rt) { if(a<=l&&b>=r){ //如果要更新的区间包含整个区间,那么不用向下更新,直接用区间长度乘以替换的值得到区间总和 lazy[rt]=x; //但是下次更新的区间可能只是整个区间的子区间,那么不把这次替换的值更新下次就丢失了这次的数据 sum[rt]=(r-l+1)*x; //所以用lazy[rt]=x 记录这次有个操作是更新了rt代表的区间的值为x,下次如果更新操作不再包含整个区间而是 return; //整个区间的子区间,未免数据丢失,先取出lazy[rt]的x,把x向下更新了。然后再执行本次操作 } pushdown(r-l+1,rt); //取出lazy[rt]执行上次的更新操作 int mid=(l+r)>>1; //然后执行本次操作 if(a<=mid) upd(a,b,x,l,mid,rt<<1); if(b>mid) upd(a,b,x,mid+1,r,rt<<1|1); pushup(rt); } int main() { scanf("%d",&t); int cas=1; while(t--){ scanf("%d%d",&n,&q); creat(1,n,1); while(q--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); upd(a,b,c,1,n,1); } printf("Case %d: The total value of the hook is %d.\n",cas++,sum[1]); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-05 11:55:17