现在的位置: 首页 > 综合 > 正文

线段树 完全版

2013年09月06日 ⁄ 综合 ⁄ 共 14000字 ⁄ 字号 评论关闭
以下均来自神牛的杰作,可作为参考:

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



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

抱歉!评论已关闭.