线段树的一般结构:
图片转自:https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html
1.pushup
把子节点的信息向上传递
2.pushdown
把信息向子节点传递
pushdown是在查和改到目标区域才向下传递,也就是这个标记很懒
用他他才向下传递,不需要每次都传递到叶子结点,避免重复多次修改叶子结点的懒惰标记,很多时候在上面就已经结束了
这就是懒惰标记
注意事项:
线段树一般要开4倍空间.
多组输入要清空懒惰标记和一些特殊标记
注意区分(a,b)和(l,r),不要混淆
线段树单点修改区间求和:
#include
#define ls l, m, rt<<1
#define rs m+1,r,rt<<1 |1
#define ll long long
#define MAXN 100005
using namespace std;
ll sum[MAXN<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&sum[rt]);
return ;
}
int m=(l+r)>>1;
build(ls);
build(rs);
pushup(rt);
}
void update(int d,int v,int l,int r,int rt)
{
if(l==r)
{
sum[rt]+=v;
return ;
}
int m=(l+r)>>1;
if(d<=m)//只要修改左区间
update(d,v,ls);
else//只要修改右区间
update(d,v,rs);
pushup(rt);
}
ll query(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return sum[rt];
}
int m=(l+r)>>1;
ll res=0;
if(a<=m)//需要查询左区间
res+=query(a,b,ls);
if(b>m)//需要查询右区间
res+=query(a,b,rs);
return res;
}
int main()
{
int n,m,x,d,v,a,b,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
build(1,n,1);
while(m--)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d%d",&d,&v);
update(d,v,1,n,1);
}
else{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
}
}
return 0;
}
单点修改,区间求极值:
#include
#define ll long long
#define MAXN 200005
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int maxs[MAXN<<2],mins[MAXN<<2];
int x;
void maxmin(int rt)
{
maxs[rt]=max(maxs[rt<<1],maxs[rt<<1|1]);
mins[rt]=min(mins[rt<<1],mins[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%d",&x);
maxs[rt]=mins[rt]=x;
return ;
}
int m=(l+r)>>1;
build(ls);
build(rs);
maxmin(rt);
}
void update(int d,int v,int l,int r,int rt)
{
if(l==r)
{
maxs[rt]+=v;
return ;
}
int m=(l+r)>>1;
if(d<=m)
update(d,v,ls);
else
update(d,v,rs);
maxmin(rt);
}
int query(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return maxs[rt];
}
int m=(l+r)>>1;
int res=0;
if(a<=m)
res=max(res,query(a,b,ls));
if(b>m)
res=max(res,query(a,b,rs));
return res;
}
int query2(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return mins[rt];
}
int m=(l+r)>>1;
int res=1e9;
if(a<=m)
res=min(res,query2(a,b,ls));
if(b>m)
res=min(res,query2(a,b,rs));
return res;
}
int main()
{
int n,m,d,a,b,v;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d%d",&d,&v);
update(d,v,1,n,1);
}
else if(x==2){
scanf("%d%d",&a,&b);
printf("%d\n",query(a,b,1,n,1));
}
else{
scanf("%d%d",&a,&b);
printf("%d\n",query2(a,b,1,n,1));
}
}
return 0;
}
区间修改(修改为定值)区间求和:
#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 100005
using namespace std;
ll sum[MAXN<<2],add[MAXN<<2];
void pushdown(int l,int r,int rt)//懒惰标记,向下推区间
{
if(add[rt])
{
add[rt<<1]=add[rt];
add[rt<<1|1]=add[rt];
int m=(l+r)>>1;
sum[rt<<1]=add[rt]*(m-l+1);
sum[rt<<1|1]=add[rt]*(r-(m+1)+1);
add[rt]=0;
}
}
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=1;
return ;
}
int m=(l+r)>>1;
build(ls);
build(rs);
pushup(rt);
}
void update(int a,int b,int v,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
sum[rt]=(r-l+1)*v;
add[rt]=v;
return ;
}
int m=(l+r)>>1;
pushdown(l,r,rt);
if(a<=m)
update(a,b,v,ls);
if(b>m)
update(a,b,v,rs);
pushup(rt);
}
ll query(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return sum[rt];
}
int m=(l+r)>>1;
pushdown(l,r,rt);
ll res=0;
if(a<=m)
res+=query(a,b,ls);
if(b>m)
res+=query(a,b,rs);
return res;
}
int main()
{
int n,m,a,b,v,x,cas=1,t;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d%d%d",&a,&b,&v);
update(a,b,v,1,n,1);
}
else{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
}
return 0;
}
POJ - 3468
区间修改(加,减),区间查询:
#include
#define ll long long
#define maxn 100007
#define ls l,m,rt<<1
#define lr m+1,r,rt<<1|1
ll sum[maxn<<2]={0},add[maxn<<2]={0};
int a[maxn];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int l,int r,int rt)//下推标记函数
{
if(add[rt])
{
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
int m=(l+r)>>1;
sum[rt<<1]+=add[rt]*(m-l+1);
sum[rt<<1|1]+=add[rt]*(r-(m+1)+1);
add[rt]=0;
}
}
void build(int l,int r,int rt)//建立树
{
if(l==r)
{
sum[rt]=a[l];
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
void update2(int L,int R,int C,int l,int r,int rt)//区间修改
{
if(L<=l&&r<=R)
{
sum[rt]+=C*(r-l+1);
add[rt]+=C;
return ;
}
int m=(l+r)>>1;
pushdown(l,r,rt);
if(L<=m) update2(L,R,C,l,m,rt<<1);
if(R>m) update2(L,R,C,m+1,r,rt<<1|1);
pushup(rt);
}
long long query(int L,int R,int l,int r,int rt)//区间查询
{
if(L<=l&&r<=R)
return sum[rt];
int m=(l+r)>>1;
pushdown(l,r,rt);
long long ans=0;
if(L<=m) ans+=query(L,R,l,m,rt<<1);
if(R>m) ans+=query(L,R,m+1,r,rt<<1|1);
return ans;
}
int main()
{
int n,m,x,y,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
char str[5];
for(int i=0;i
区间修改,区间极值
#include
#define MAXN 200005
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int maxs[MAXN<<2];
int add[MAXN<<2];
void push_up(int rt)
{
maxs[rt]=max(maxs[rt<<1],maxs[rt<<1|1]);
}
void pushdown(int l,int r,int rt)
{
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
maxs[rt<<1]+=add[rt];
maxs[rt<<1|1]+=add[rt];
add[rt]=0;
}
}
void build(int l,int r,int rt)
{
if(l==r){
maxs[rt]=0;
return ;
}
int m=(l+r)>>1;
build(ls);
build(rs);
}
void update(int a,int b,int c,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
add[rt]+=c;
maxs[rt]+=c;
return ;
}
int m=(l+r)>>1;
pushdown(l,r,rt);
if(a<=m)
update(a,b,c,ls);
if(b>m)
update(a,b,c,rs);
push_up(rt);
}
int query(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return maxs[rt];
}
int m=(l+r)>>1;
pushdown(l,r,rt);
int ans=0;
if(a<=m)
ans=max(ans,query(a,b,ls));
if(b>m)
ans=max(ans,query(a,b,rs));
return ans;
}
int main()
{
int n,m,l,r,x,v;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
scanf("%d",&x);
if(x==1){//修改
scanf("%d%d%d",&l,&r,&v);
update(l,r,1,v,n,1);
}
else{//查询
scanf("%d%d",&l,&r);
printf("%d\n",query(l,r,1,n,1));
}
}
return 0;
}
区间连续最长上升子序列长度,单点修改,区间查询
区间合
ac:
#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 200005
using namespace std;
int lsum[MAXN<<2];
int rsum[MAXN<<2];
int msum[MAXN<<2];
int num[MAXN];
int x;
void pushup(int l,int r,int rt)
{
int m=(l+r)>>1;
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
msum[rt]=max(msum[rt<<1],msum[rt<<1|1]);
if(num[m]>1;
build(ls);
build(rs);
pushup(l,r,rt);
}
void update(int d,int v,int l,int r,int rt)
{
if(l==r)
{
num[d]=v;
return ;
}
int m=(l+r)>>1;
if(m>=d)
update(d,v,ls);
else
update(d,v,rs);
pushup(l,r,rt);
}
int query(int a,int b,int l,int r,int rt)
{
if(a<=l&&r<=b)
{
return msum[rt];
}
int res=0;
int m=(l+r)>>1;
if(m>=a)
res=max(res,query(a,b,ls));
if(b>m)
res=max(res,query(a,b,rs));
if(num[m]
D - Welfare State
解析:
长n的数组,q次操作
最后单点查询n次
q:1 x v 将x修改为v
q: 2 x 修改小于x的所以a[i]为x
写的时候,没有注意pushdown的add[rt]不一定比add[rt<<1]和add[rt<<1|1]大
因为每次pushdown不一定进行到底部,要判断是否变
ac:
#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 400005
using namespace std;
ll sum[MAXN<<2],add[MAXN<<2];
ll aa[MAXN<<2];
void pushdown(ll l,ll r,ll rt)//懒惰标记,向下推区间
{
if(add[rt])
{
add[rt<<1]=max(add[rt],add[rt<<1]);//注意这里,pushdown并不是每次都自底到下,所以这里也要判断是否变小
add[rt<<1|1]=max(add[rt],add[rt<<1|1]);
ll m=(l+r)>>1;
if(m-l+1<=1)
sum[rt<<1]=max(add[rt],sum[rt<<1]);
else sum[rt<<1]=add[rt]*(m-l+1);
if((r-(m+1)+1)<=1)
sum[rt<<1|1]=max(sum[rt<<1|1],add[rt]);
else sum[rt<<1|1]=add[rt]*(r-(m+1)+1);
add[rt]=0;
}
}
void build(ll l,ll r,ll rt)
{
if(l==r)
{
sum[rt]=aa[l];
return ;
}
ll m=(l+r)>>1;
build(ls);
build(rs);
}
void update(ll a,ll b,ll v,ll l,ll r,ll rt)
{
if(a<=l&&r<=b)
{
sum[rt]=max(v,sum[rt]);
add[rt]=max(v,add[rt]);
return ;
}
}
void update2(ll a,ll b,ll v,ll l,ll r,ll rt)
{
if(a<=l&&r<=b)
{
sum[rt]=v;
return ;
}
ll m=(l+r)>>1;
pushdown(l,r,rt);
if(a<=m)
update2(a,b,v,ls);
if(b>m)
update2(a,b,v,rs);
}
ll query(ll a,ll b,ll l,ll r,ll rt)
{
if(a<=l&&r<=b)
{
return sum[rt];
}
ll m=(l+r)>>1;
pushdown(l,r,rt);
ll res=0;
if(a<=m)
res+=query(a,b,ls);
if(b>m)
res+=query(a,b,rs);
return res;
}
int main()
{
ll n,m,a,b,v,x,cas=1,t;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&aa[i]);
build(1,n,1);
scanf("%lld",&m);
while(m--)
{
scanf("%lld",&x);
if(x==1)
{
scanf("%lld%lld",&a,&v);
update2(a,a,v,1,n,1);
}
else{
scanf("%lld",&v);
update(1,n,v,1,n,1);
}
}
for(ll i=1;i<=n;i++)
printf("%lld ",query(i,i,1,n,1));
return 0;
}
矩形面积并:https://vjudge.net/problem/POJ-1151
解析:
扫描线+线段树
将上边,下边离散化后排序,按l排序,保存高度,和如果是下边标记1,上边标记-1
然后从下往上一层一层的计算
代码:
#include
#include
#include
#include
#include
#include
#define MAXN 505
using namespace std;
int add[MAXN<<2];
double x[MAXN<<2],sum[MAXN<<2];
void init()
{
memset(add,0,sizeof(add));
memset(sum,0,sizeof(sum));
memset(x,0,sizeof(x));
}
struct node
{
double l,r,h;
int d;
friend bool operator <(node a,node b)
{
return a.h>1;
if(L<=m)
update(L,R,c,l,m,rt<<1);
if(R>m)
update(L,R,c,m+1,r,rt<<1|1);
pushup(l,r,rt);
}
int main()
{
int t,n,cas=1;
double a,b,c,d;
while(scanf("%d",&n)&&n)
{
init();
int num=0,k=1;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
x[++num]=a;
seg[num]=node{a,c,b,1};
x[++num]=c;
seg[num]=node{a,c,d,-1};
}
sort(x+1,x+num+1);
sort(seg+1,seg+num+1);
k=unique(x+1,x+num+1)-x-1;
double ans=0;
for(int i=1;i<=num-1;i++)//从下往上一层一层计算
{
int l=lower_bound(x+1,x+k+1,seg[i].l)-x;
int r=lower_bound(x+1,x+k+1,seg[i].r)-x-1;
update(l,r,seg[i].d,1,k,1);
ans+=sum[1]*(seg[i+1].h-seg[i].h);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ans);
}
return 0;
}
/*
2
10 10 20 20
15 15 25 25.5
*/