有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;//add
case 'e':scanf("%lld",&z);set(1,x,y,z);break;//set
case 'u':printf("%lld\n",sum(1,x,y));break;//sum
case 'a':printf("%lld\n",findmx(1,x,y));break;//max
case '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……),跟它本身节点的值已经没关系了,他本身节点的值已经更新过了。