以下均来自神牛的杰作,可作为参考:
关于线段树的功能基本都在下面阐述,衷心感谢
单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来
Hdu1754 I hate it
线段树功能:update:单点替换 query:区间最值
#include<iostream>
#include<algorithm>
using namespace std;
int MAXN[4000001],d[4000001];
void PushUp(int rt)
{
MAXN[rt]=max(MAXN[rt<<1],MAXN[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
cin>> MAXN[rt];
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)+1);
PushUp(rt);
}
void update(int p,int sc,int l,int r,int rt)
{
if(l==r)
{
MAXN[rt]=sc;
return;
}
int m=(l+r)>>1;
if(p<=m)update(p,sc,l,m,rt<<1);
else update(p,sc,m+1,r,(rt<<1)+1);
PushUp(rt);
}
int getmax(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return MAXN[rt];
}
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret=max(ret,getmax(L,R,l,m,rt<<1));
if(R>m)ret=max(ret,getmax(L,R,m+1,r,(rt<<1)+1));
return ret;
}
int main()
{
int n,m,a,b;
char ch;
while(cin>>n>>m)
{
build(1,n,1);
for(int i=0;i<m;i++)
{
cin>>ch>>a>>b;
if(ch=='U')update(a,b,1,n,1);
else cout<<getmax(a,b,1,n,1)<<endl;
}
}
}
hdu1166敌兵布阵
线段树功能:update:单点增减 query:区间求和
#include<iostream>
#include<string>
using namespace std;
const int maxn=55555;
int 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)
{
cin>>sum[rt];
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)+1);
PushUp(rt);
}
void update(int p,int add,int l,int r,int rt)
{
if(l==r)
{
sum[rt]+=add;
return;
}
int m=(l+r)>>1;
if(p<=m)update(p,add,l,m,rt<<1);
else update(p,add,m+1,r,(rt<<1)+1);
PushUp(rt);
}
int getsum(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return sum[rt];
}
int m=(r+l)>>1;
int ret=0;
if(L<=m)ret+=getsum(L,R,l,m,rt<<1);
if(R>m)ret+=getsum(L,R,m+1,r,(rt<<1)+1);
return ret;
}
int main()
{
int cas,n,a,b,k=0;
string s;
cin>>cas;
while(cas--)
{
k++;
cout<<"Case "<<k<<":"<<endl;
cin>>n;
build(1,n,1);
while(cin>>s)
{
if(s=="End")break;
if(s=="Query")
{
cin>>a>>b;
cout<<getsum(a,b,1,n,1)<<endl;
}
else if(s=="Add")
{
cin>>a>>b;
update(a,b,1,n,1);
}
else
{
cin>>a>>b;
update(a,-b,1,n,1);
}
}
}
}
hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和
#include<iostream>
#include<algorithm>
using namespace std;
int sum[10001],x[5001];
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[(rt<<1)+1];
}
void build(int l,int r,int rt)
{
sum[rt]=0;
if(l==r)return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)+1);
}
void update(int p,int l,int r,int rt)
{
if(l==r)
{
sum[rt]++;return;
}
int m=(l+r)>>1;
if(p<=m)update(p,l,m,rt<<1);
else update(p,m+1,r,(rt<<1)+1);
PushUp(rt);
}
int GetReverse(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return sum[rt];
int m=(l+r)>>1;
int ret=0;
if(L<=m)ret+=GetReverse(L,R,l,m,rt<<1);
if(R>m)ret+=GetReverse(L,R,m+1,r,(rt<<1)+1);
return ret;
}
int main()
{
int n;
while(cin>>n)
{
build(0,n-1,1);
int sum=0;
for(int i=0;i<n;i++)
{
cin>>x[i];
sum+=GetReverse(x[i],n-1,0,n-1,1);//求x[i]到n-1之间已存在几个数字,即求x[i]的逆序数
update(x[i],0,n-1,1);//包含[x[i],x[i]]的区间+1
}
int ret=sum;
for(int i=0;i<n;i++)
{//把第一个位子上的数(x[i])移到最后,则被移的数的逆序是n-1-x[i],而未被移的数字有x[i]个比x[i]小
sum+=n-1-x[i]-x[i];
ret=min(sum,ret);
}
cout<<ret<<endl;
}
}
hdu2795Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)
#include<iostream>
#include<algorithm>
using namespace std;
int MAX[222222<<2];
int w;
void PushUp(int rt)
{
MAX[rt]=max(MAX[rt<<1],MAX[(rt<<1)|1]);
}
void build(int l,int r,int rt)
{
MAX[rt]=w;//c初始化 每行都有W的空间
if(l==r)return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1|1));
}
int query(int x,int l,int r,int rt)
{
if(l==r){MAX[rt]-=x;return l;}
int m=(l+r)>>1;
int ret=(MAX[rt<<1] >= x? query(x ,l,m,rt<<1):query(x ,m+1,r,(rt<<1)|1)) ;//查找左边能放得下,优先左边
PushUp(rt);
return ret;
}
int main()
{
int h,n,x;
while(scanf("%d%d%d",&h,&w,&n)==3)
{
if(h>n)h=n;//题目中h>1,n<200000,h可能会大于所开的MAX[];
build(1,h,1);
for(int i=0;i<n;i++)
{
scanf("%d",&x);
if(x>MAX[1])printf("%d\n",-1);
else printf("%d\n",query(x,1,h,1));
}
}
}
成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
hdu1698Just a Hook
题意:O(-1)
思路:O(-1)
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 111111;
int col[maxn<<2];
int sum[maxn<<2];
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[(rt<<1)|1];
}
void PushDown(int rt,int m)
{
if(col[rt])
{
col[(rt<<1)|1]=col[rt<<1]=col[rt];
sum[rt<<1]=col[rt]*(m-(m>>1));
sum[(rt<<1)|1]=col[rt]*(m>>1);
col[rt]=0;
}
}
void build(int l,int r,int rt)
{
col[rt]=0;sum[rt]=1;
if(l==r)return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)|1);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
col[rt]=c;
sum[rt]=(r-l+1)*c;
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>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(rt);
}
int main()
{
int cas,n,m,a,b,c,k=0;
scanf("%d",&cas);
while(cas--)
{
k++;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
update(a,b,c,1,n,1);
}
printf("Case %d: The total value of the hook is %d.\n",k,sum[1]);
}
}
3468A Simple Problem with Integers
线段树功能:update:成段增减 query:区间求和
#include<iostream>
#include <algorithm>
using namespace std;
const int maxn = 111111;
long long int add[maxn<<2];
long long int sum[maxn<<2];
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[(rt<<1)|1];
}
void PushDown(int rt,int m)
{
if(add[rt])
{
add[rt<<1]+=add[rt];
add[(rt<<1)|1]+=add[rt];
sum[rt<<1]+=add[rt]*(m-(m>>1));
sum[(rt<<1)|1]+=add[rt]*(m>>1);
add[rt]=0;
}
}
void build(int l,int r,int rt)
{
add[rt]=0;
if(l==r)
{
cin>>sum[rt];
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)|1);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
add[rt]+=c;
sum[rt]+=(long long int)(r-l+1)*c;
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>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(rt);
}
long long int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return sum[rt];
PushDown(rt,r-l+1);
int m=(l+r)>>1;
long long int ret=0;
if(L<=m)ret+=query(L,R,l,m,rt<<1);
if(m<R)ret+=query(L,R,m+1,r,(rt<<1)|1);
return ret;
}
int main()
{
int n,m,a,b,c;
char ch;
while(cin>>n>>m)
{
build(1,n,1);
for(int i=0;i<m;i++)
{
cin>>ch;
if(ch=='C')
{
cin>>a>>b>>c;
update(a,b,c,1,n,1);
}
else {cin>>a>>b;cout<<query(a,b,1,n,1)<<endl;}
}
}
}
poj2528Mayor's posters
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
思路:这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
而这题的难点在于每个数字其实表示的是一个单位长度(并且一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
给出下面两个简单的例子应该能体现普通离散化的缺陷:
1-10 1-4 5-10
1-10 1-4 6-10
为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]
如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.
线段树功能:update:成段替换 query:简单hash
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=11111;
int col[maxn<<4],le[maxn],ri[maxn],X[maxn*3],hash[maxn];
int ans;
void PushDown(int rt)
{
if(col[rt]!=-1)
{
col[rt<<1]=col[(rt<<1)|1]=col[rt];
col[rt]=-1;
}
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
col[rt]=c;
return;
}
PushDown(rt);
int m=(l+r)>>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);
}
void query(int l,int r,int rt)
{
if(col[rt]!=-1)
{
if(!hash[col[rt]]){ans++;hash[col[rt]]=1;}
return;
}
if(l==r)return;
int m=(l+r)>>1;
query(l,m,rt<<1);
query(m+1,r,(rt<<1)|1);
}
int Bin(int key,int n)
{
int l=0,r=n-1;
while(l<=r)
{
int m=(l+r)>>1;
if(X[m]==key)return m;
if(X[m]>key) r=m-1;
else l=m+1;
}
return -1;
}
int main()
{
int cas,n;
cin>>cas;
while(cas--)
{
cin>>n;
int nn=0;
for(int i=0;i<n;i++)
{
cin>>le[i]>>ri[i];
X[nn++]=le[i],X[nn++]=ri[i];
}
sort(X,X+nn);
int m=1;
for(int i=1;i<nn;i++)if(X[i]!=X[i-1])X[m++]=X[i];
for(int i=m-1;i>0;i--)if(X[i]!=X[i-1]+1)X[m++]=X[i-1]+1;
sort(X,X+m);
memset(col,-1,sizeof(col));
for(int i=0;i<n;i++)
{
int l=Bin(le[i],m);
int r=Bin(ri[i],m);
update(l,r,i,0,m,1);
}
ans=0;
memset(hash,0,sizeof(hash));
query(0,m,1);
cout<<ans<<endl;
}
}
区间合并这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并
poj3667Hotel
题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空
思路:记录区间中最长的空房间
线段树操作:update:区间替换 query:询问满足条件的最左断点
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50000;
int cover[maxn<<2];
int lsum[maxn<<2],rsum[maxn<<2],msum[maxn<<2];
void PushUp(int rt,int m)
{
lsum[rt]=lsum[rt<<1],rsum[rt]=rsum[(rt<<1)|1];
if(lsum[rt<<1]==m-(m>>1))lsum[rt]+=lsum[(rt<<1)|1];
if(rsum[(rt<<1)|1]==m>>1)rsum[rt]+=rsum[rt<<1];
msum[rt]=max(max(msum[rt<<1],msum[(rt<<1)|1]),rsum[rt<<1]+lsum[(rt<<1)|1]);
}
void PushDown(int rt,int m)
{
if(cover[rt]!=-1)
{
cover[rt<<1]=cover[(rt<<1)|1]=cover[rt];
lsum[rt<<1]=rsum[rt<<1]=msum[rt<<1]=(cover[rt]? 0:m-(m>>1));
lsum[(rt<<1)|1]=rsum[(rt<<1)|1]=msum[(rt<<1)|1]=(cover[rt]? 0:m>>1);
cover[rt]=-1;
}
}
void build(int l,int r,int rt)
{
cover[rt]=-1;
lsum[rt]=rsum[rt]=msum[rt]=r-l+1;
if(l==r)return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)|1);
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
cover[rt]=c;
lsum[rt]=rsum[rt]=msum[rt]=(c? 0:r-l+1);
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>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(rt,r-l+1);
}
int query(int w,int l,int r,int rt)
{
if(l==r)return l;
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(msum[rt<<1]>=w)return query(w,l,m,rt<<1);
else if(rsum[rt<<1]+lsum[(rt<<1)|1]>=w)return m-rsum[rt<<1]+1;
else query(w,m+1,r,(rt<<1)|1);
}
int main()
{
int n,m,a,b,c;
while(cin>>n>>m)
{
build(1,n,1);
while(m--)
{
cin>>a;
if(a==1)
{
cin>>b;
if(msum[1]<b){cout<<0<<endl;continue;}
int ans=query(b,1,n,1);
cout<<ans<<endl;
update(ans,ans+b-1,1,1,n,1);
}
else
{
cin>>b>>c;
update(b,b+c-1,0,1,n,1);
}
}
}
}
hdu3911Black And White
题意:给出01串 1代表黑色 0代表白色
0 a b:询问[a,b]中1的连续最大长度
1 a,b:把[a,b]区间的0->1 1->0
思路:lsum1[],rsum1[],msum1[]分别表示区间做起连续1的最大长度,右起连续1的最大长度,区间1的最大连续长度
lsum0[],rsum0[],msum0[]分别表示区间做起连续0的最大长度,右起连续0的最大长度,区间连续0的最大连续长度
0 a b:交换 0和1的lsum[] rsum[] msum[]; ROX[]表示需要向儿子更新 两次更新=不变
线段树功能:update区间替换,query询问区间1的最大连续长度
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=100001;
int ROX[maxn<<2];
int lsum1[maxn<<2],rsum1[maxn<<2],msum1[maxn<<2];
int lsum0[maxn<<2],rsum0[maxn<<2],msum0[maxn<<2];
void PushUp(int rt,int m)
{
lsum1[rt]=lsum1[rt<<1],rsum1[rt]=rsum1[(rt<<1)|1];
if(lsum1[rt<<1]==m-(m>>1))lsum1[rt]+=lsum1[(rt<<1)|1];
if(rsum1[(rt<<1)|1]==m>>1)rsum1[rt]+=rsum1[rt<<1];
msum1[rt]=max(max(msum1[rt<<1],msum1[(rt<<1)|1]),rsum1[rt<<1]+lsum1[(rt<<1)|1]);
lsum0[rt]=lsum0[rt<<1],rsum0[rt]=rsum0[(rt<<1)|1];
if(lsum0[rt<<1]==m-(m>>1))lsum0[rt]+=lsum0[(rt<<1)|1];
if(rsum0[(rt<<1)|1]==m>>1)rsum0[rt]+=rsum0[rt<<1];
msum0[rt]=max(max(msum0[rt<<1],msum0[(rt<<1)|1]),rsum0[rt<<1]+lsum0[(rt<<1)|1]);
}
void PushDown(int rt,int m)
{
if(ROX[rt])
{
ROX[rt<<1]^=1,ROX[(rt<<1)|1]^=1;
swap(lsum0[rt<<1],lsum1[rt<<1]),swap(rsum1[rt<<1],rsum0[rt<<1]),swap(msum1[rt<<1],msum0[rt<<1]);
swap(lsum0[(rt<<1)|1],lsum1[(rt<<1)|1]), swap(rsum1[(rt<<1)|1],rsum0[(rt<<1)|1]), swap(msum1[(rt<<1)|1],msum0[(rt<<1)|1]);
ROX[rt]=0;
}
}
void build(int l,int r,int rt)
{
ROX[rt]=0;
if(l==r)
{
int c;
scanf("%d",&c);
lsum1[rt]=rsum1[rt]=msum1[rt]=c;
lsum0[rt]=rsum0[rt]=msum0[rt]=c^1;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,(rt<<1)|1);
PushUp(rt,r-l+1);
}
void update(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
ROX[rt]^=1;
swap(lsum0[rt],lsum1[rt]),swap(rsum1[rt],rsum0[rt]),swap(msum1[rt],msum0[rt]);
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m)update(L,R,l,m,rt<<1);
if(R>m)update(L,R,m+1,r,(rt<<1)|1);
PushUp(rt,r-l+1);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return msum1[rt];
int tmp;
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(m>=R) tmp=query(L,R,l,m,rt<<1);
else if(m<L)tmp=query(L,R,m+1,r,(rt<<1)|1);
else
{
tmp=max(query(L,R,l,m,rt<<1),query(L,R,m+1,r,(rt<<1)|1));
int tmp1=min(m-L+1,rsum1[rt<<1]);
int tmp2=min(R-m,lsum1[(rt<<1)|1]);
tmp=max(tmp,tmp1+tmp2);
}
return tmp;
}
int main()
{
int n,m,a,b,c;
while(scanf("%d",&n)==1)
{
build(1,n,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(a==1)update(b,c,1,n,1);
else cout<<query(b,c,1,n,1)<<endl;
}
}
}
扫描线
这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去
最典型的就是矩形面积并,周长并等题
hdu1542Atlantis
题意:矩形面积并
思路:浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个
线段树操作:update:区间增减 query:直接取根节点的值
#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
using namespace std;
const int maxn=201;
int cnt[maxn<<2];
double X[maxn<<1],sum[maxn<<2];
struct Seg
{
double l,r,h;
int c;
Seg(){};
Seg(double _l,double _r,double _h,int _c):l(_l),r(_r),h(_h),c(_c){};
bool operator <(const Seg& cmp)
{
return h<cmp.h;
}
}s[maxn<<2];
void PushUp(int rt,int l,int r)
{
if(cnt[rt])sum[rt]=X[r+1]-X[l];
else if(l==r)sum[rt]=0;
else sum[rt]=sum[rt<<1]+sum[(rt<<1)|1];
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
cnt[rt]+=c;
PushUp(rt,l,r);
return;
}
int m=(l+r)>>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(rt,l,r);
}
int Bin(double key,int n)
{
int l=0,r=n-1;
while(l<=r)
{
int m=(l+r)>>1;
if(X[m]==key)return m;
if(key<X[m])r=m-1;
else l=m+1;
}
return -1;
}
int main()
{
cout.precision(2);
int n,cas=0;
double a,b,c,d;
while(cin>>n&&n)
{
cas++;
int m=0;
for(int i=0;i<n;i++)
{
cin>>a>>b>>c>>d;
s[m]=Seg(a,c,b,1);
X[m++]=a;
s[m]=Seg(a,c,d,-1);
X[m++]=c;
}
sort(X,X+m);
sort(s,s+m);
int k=1;
double ans=0;
for(int i=1;i<m;i++)if(X[i]!=X[i-1])X[k++]=X[i];
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
for(int i=0;i<m;i++)
{
int l=Bin(s[i].l,k);
int r=Bin(s[i].r,k)-1;
if (l <= r) update(l,r,s[i].c,0,k-1,1);
ans+=sum[1]*(s[i+1].h-s[i].h);
}
cout<<"Test case #"<<cas<<endl;
cout<<"Total explored area: "<<fixed<<ans<<endl;
cout<<endl;
}
}
hdu1828Picture
题意:矩形周长并
思路:与面积不同的地方是还要记录竖的边有几个(numseg记录),并且当边界重合的时候需要合并(用lbd和rbd表示边界来辅助)
线段树操作:update:区间增减 query:直接取根节点的值
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=20001;
int len[maxn<<2],lbd[maxn<<2],rbd[maxn<<2],cnt[maxn<<2],numSeg[maxn<<2];
struct Seg
{
int l,r,h,c;
Seg(){};
Seg(int _l,int _r,int _h,int _c):l(_l),r(_r),h(_h),c(_c){};
bool operator<(Seg&a)
{
return h<a.h;
}
}s[maxn];
void PushUp(int rt,int l,int r)
{
if(cnt[rt])
{
lbd[rt]=rbd[rt]=1;
len[rt]=r-l+1;
numSeg[rt]=2;
}
else if(l==r)
{
lbd[rt]=rbd[rt]=len[rt]=numSeg[rt]=0;
}
else
{
lbd[rt]=lbd[rt<<1],rbd[rt]=rbd[(rt<<1)|1];
len[rt]=len[rt<<1]+len[(rt<<1)|1];
numSeg[rt]=numSeg[rt<<1]+numSeg[(rt<<1)|1];
if(rbd[rt<<1]&&lbd[(rt<<1)|1])numSeg[rt]-=2;
}
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
cnt[rt]+=c;
PushUp(rt,l,r);
return;
}
int m=(l+r)>>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(rt,l,r);
}
int main()
{
int n,a,b,c,d;
while(cin>>n)
{
int m=0,lmin=10000,rmax=-10000;
for(int i=0;i<n;i++)
{
cin>>a>>b>>c>>d;
lmin=min(lmin,a);
rmax=max(rmax,c);
s[m++]=Seg(a,c,b,1);
s[m++]=Seg(a,c,d,-1);
}
sort(s,s+m);
int ret=0,last=0;
for(int i=0;i<m;i++)
{
if (s[i].l < s[i].r)update(s[i].l,s[i].r-1,s[i].c,lmin,rmax-1,1);
ret+=numSeg[1]*(s[i+1].h-s[i].h);
ret+=abs(len[1]-last);
last=len[1];
}
cout<<ret<<endl;
}
}