codevs线段树练习5(双重标记)

来源:互联网 发布:jquery 对象数组 编辑:程序博客网 时间:2024/06/05 08:05

有n个数和5种操作

add a b c:把区间[a,b]内的所有数都增加c

set a b c:把区间[a,b]内的所有数都设为c

sum a b:查询区间[a,b]的区间和

max a b:查询区间[a,b]的最大值

min a,b:查询区间[a,b]的最小值

双标记,注意双标记间的关系即可,注意一下,不是很难,顺便练代码能力,可惜一次编完,还是有两个地方没考虑到

对于线段树add的部分,以后一定要再注意再注意!add可能会多次,所以要用+=

a【i】.add+=add;!!!!

set用一个bool来判断是否被区间赋值,更方便,且不会有bug。

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<cstdlib>using namespace std;typedef long long ll;const ll inf=100000000000ll;//pay attention to 'll'int n,m;struct aa{ll mx,mi,sum,add;int l,r,set;}a[400005+1000];void up(int i){a[i].mi=inf;a[i].mx=-inf;a[i].sum=0;if (a[i<<1].l){a[i].mi=min(a[i].mi,a[i<<1].mi);a[i].mx=max(a[i].mx,a[i<<1].mx);a[i].sum+=a[i<<1].sum;}if (a[i<<1|1].l){a[i].mi=min(a[i].mi,a[i<<1|1].mi);a[i].mx=max(a[i].mx,a[i<<1|1].mx);a[i].sum+=a[i<<1|1].sum;}}void down(int i){if (a[i].set){a[i<<1].set=a[i<<1|1].set=1;a[i<<1].add=a[i<<1|1].add=0;ll st=a[i].mi;a[i<<1].sum=st*(a[i<<1].r-a[i<<1].l+1);a[i<<1|1].sum=st*(a[i<<1|1].r-a[i<<1|1].l+1);a[i<<1].mi=a[i<<1|1].mi=a[i<<1].mx=a[i<<1|1].mx=st;a[i].set=0;}if (a[i].add){ll ad=a[i].add;a[i<<1].mi+=ad;a[i<<1].mx+=ad;a[i<<1].sum+=(a[i<<1].r-a[i<<1].l+1)*ad;if (a[i<<1].set==0) a[i<<1].add+=ad; a[i<<1|1].mi+=ad;a[i<<1|1].mx+=ad;a[i<<1|1].sum+=(a[i<<1|1].r-a[i<<1|1].l+1)*ad;if (a[i<<1|1].set==0) a[i<<1|1].add+=ad;//这个地方一定要用  += ,因为可能重复加//这个错误犯了好多次了!!,要非常注意 a[i].add=0;}}void build(int i,int l,int r){a[i].l=l;a[i].r=r;if (l==r){ll x;scanf("%lld",&x);a[i].mx=a[i].mi=a[i].sum=x;return ;}int mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);up(i);}void add(int i,int l,int r,ll ad){if (a[i].l==l&&a[i].r==r){a[i].sum+=(a[i].r-a[i].l+1)*ad;a[i].mx+=ad;a[i].mi+=ad;if (a[i].set) return ;a[i].add+=ad;//这个地方一定要用  += ,因为可能重复加return ;}down(i);int mid=(a[i].l+a[i].r)>>1;if (mid>=r) add(i<<1,l,r,ad);else if (mid<l) add(i<<1|1,l,r,ad);else add(i<<1,l,mid,ad),add(i<<1|1,mid+1,r,ad);up(i);}void set(int i,int l,int r,ll st){if (a[i].l==l&&a[i].r==r){a[i].set=1;a[i].sum=st*(a[i].r-a[i].l+1);a[i].mx=a[i].mi=st;a[i].add=0;return ;}down(i);int mid=(a[i].l+a[i].r)>>1;if (mid>=r) set(i<<1,l,r,st);else if (mid<l) set(i<<1|1,l,r,st);else set(i<<1,l,mid,st),set(i<<1|1,mid+1,r,st);up(i);}ll sum(int i,int l,int r){if (a[i].l==l&&a[i].r==r) return a[i].sum;down(i);int mid=(a[i].l+a[i].r)>>1;if (mid>=r) return sum(i<<1,l,r);else if (mid<l) return sum(i<<1|1,l,r);return sum(i<<1,l,mid)+sum(i<<1|1,mid+1,r);}ll findmx(int i,int l,int r){if (a[i].l==l&&a[i].r==r) return a[i].mx;down(i);int mid=(a[i].l+a[i].r)>>1;if (mid>=r) return findmx(i<<1,l,r);else if (mid<l) return findmx(i<<1|1,l,r);return max(findmx(i<<1,l,mid),findmx(i<<1|1,mid+1,r));}ll findmi(int i,int l,int r){if (a[i].l==l&&a[i].r==r) return a[i].mi;down(i);int mid=(a[i].l+a[i].r)>>1;if (mid>=r) return findmi(i<<1,l,r);else if (mid<l) return findmi(i<<1|1,l,r);return min(findmi(i<<1,l,mid),findmi(i<<1|1,mid+1,r));}int main(){scanf("%d%d",&n,&m);build(1,1,n);char s[10];int x,y;ll z;while (m--){scanf("%s",s);scanf("%d%d",&x,&y);switch(s[1]){case 'd':scanf("%lld",&z);add(1,x,y,z);break;//addcase 'e':scanf("%lld",&z);set(1,x,y,z);break;//setcase 'u':printf("%lld\n",sum(1,x,y));break;//sumcase 'a':printf("%lld\n",findmx(1,x,y));break;//maxcase 'i':printf("%lld\n",findmi(1,x,y));break;//min}}return 0;}


总结

1:关于线段树,splay,lct等等的数据结构中,太多都要用到up和down函数,关于down的顺序其实是需要注意的。

首先我们在打标记的时候,同时就要更新标记本身这个节点的答案(such as sum【i】,mx【i】)并不是要等到,push_down的时候再处理本身这个节点的,push_down的时候只处理他的孩子节点(的sum,mx……),跟它本身节点的值已经没关系了,他本身节点的值已经更新过了。


0 0
原创粉丝点击