您的位置:首页 > 其它

线段树 完全版

2012-08-12 22:09 363 查看
以下均来自神牛的杰作,可作为参考:

关于线段树的功能基本都在下面阐述,衷心感谢

单点更新:最最基础的线段树,只更新叶子节点,然后把信息用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;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: