码迷,mamicode.com
首页 > 其他好文 > 详细

线段树入门(poj 3274 3468 2528)

时间:2014-08-23 16:55:31      阅读:428      评论:0      收藏:0      [点我收藏+]

标签:poj   线段树   二叉树   

概念:

在一类问题中,我们需要经常处理可以映射在一个坐标轴上的一些固定线段,例如说映射在OX轴上的线段。由于线段是可以互相覆盖的,有时需要动态地取线段的并,例如取得并区间的总长度,或者并区间的个数等等。一个线段是对应于一个区间的,因此线段树也可以叫做区间树。

线段树常用于区间多次插入查询,经常改变数据。

而线段树的核心在于如何设计一个节点的信息

这里针对线段树的应用有三个方面:

1. 每次查询一个区间的最大差值(结点存放这个区间的最大最小值 3274)

2.不断修改区间内的数值,并查询区间的和

这里涉及到了修改,如果每次一修改就递归修改到叶子,效率非常低。所以在结点中设计一个inc,即加量。这个区间每个元素都应该添加的。只有当查询到了这个区间的时候才会递归修改下部的数值。

3.线段离散化。

离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:

有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。

现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9

然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9

对其升序排序,得2 3 4 6 8 9 10

然后建立映射

2     3     4     6     8     9   10

↓     ↓      ↓     ↓     ↓     ↓     ↓

1     2     3     4     5     6     7

那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的

离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。

数据存储:

链式或者数组形式,因为是类完全二叉树所以可以用数组存储,且方便定位。假设区间元素有N,则总结点个数为2^(logN+1),实测发现一般4*N就够了


POJ 3274

/*
核心函数
1.buildtree :递归生成,自下向上建立。即利用下层数据。孩子结点为loc<<1,loc<<1|1
2.query:这个函数还是很有技巧性,每次查询先分区间类型(包含,无交集,部分涵盖) 无交集直接pass,包含则直接提取信息。部分涵盖则判断与Mid的关系。
看是否可以完全转到另一个分支中去
3.巧用位运算,加速程序。左右孩子分别为Loc<<1,loc<<1|1
*/
#include <stdio.h>
#include <string.h>
#define INF 10000000
#define maxn 200005
#define max(a,b)  (a)>(b)?(a):(b)
#define min(a,b)  (a)<(b)?(a):(b)
struct node
{
	int l,r;
	int mn,mx;
}nodes[3*maxn];
int a[maxn];
int qmin,qmax;

void buildtree(int loc,int l,int r)
{
	int mid;
	nodes[loc].l=l;nodes[loc].r=r;
	if(l==r)
		nodes[loc].mn=nodes[loc].mx=a[l];
	else
	{
		mid=(l+r)>>1;
		buildtree(loc<<1,l,mid);
		buildtree(loc<<1|1,mid+1,r);
		nodes[loc].mn=min(nodes[loc<<1].mn,nodes[loc<<1|1].mn);
		nodes[loc].mx=max(nodes[loc<<1].mx,nodes[loc<<1|1].mx);
	}
}

void query(int loc,int l,int r)
{
	if(nodes[loc].mn>=qmin && nodes[loc].mx<=qmax)
		return;
	if(nodes[loc].l==l && nodes[loc].r==r)
	{
		qmin=min(nodes[loc].mn,qmin);
		qmax=max(nodes[loc].mx,qmax);
	}
	else
	{
		int mid;
		mid=(nodes[loc].l+nodes[loc].r)>>1;
		if(r<=mid) query(loc<<1,l,r);
		else if(l>mid) query(loc<<1|1,l,r);
		else
		query(loc<<1,l,mid),query(loc<<1|1,mid+1,r);
	}
}
int main()
{
	int N,C;
	int e,r;
	while(scanf("%d%d",&N,&C)!=EOF)
	{
		int i;
		for(i=1;i<=N;i++)
			scanf("%d",a+i);
		buildtree(1,1,N);
		for(i=0;i<C;i++)
		{
			scanf("%d%d",&e,&r);
			qmax=-INF;qmin=INF;
			query(1,e,r);
			printf("%d\n",qmax-qmin);
		}
	}
	return 0;
}
POJ 3468

延迟更新

/*
Add函数每次添加都只添加到该区间,并不递归全部更新(延缓更新)
在查询的过程中才会更新
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#define INF 10000000
#define maxn 100000
#define max(a,b)  (a)>(b)?(a):(b)
#define min(a,b)  (a)<(b)?(a):(b)
using namespace std;
struct node
{
	int l,r;
	__int64 sum;
	__int64 inc;
}nodes[3*maxn];//结点设计;左右区间,待增加量,和值
int num[maxn];
int qmin,qmax;
void add(int i,int a,int b,__int64 c)
{
	 if(nodes[i].l==a&&nodes[i].r==b)
    {
        nodes[i].inc+=c;
        return;
    }
    nodes[i].sum+=c*(b-a+1);//运行到这里,表明这个是大区间,包含添加的区间所以sum是他们的个数之和
    int mid=(nodes[i].l+nodes[i].r)>>1;
    if(b<=mid)  add(i<<1,a,b,c);
    else if(a>mid)  add(i<<1|1,a,b,c);
    else
    {
        add(i<<1,a,mid,c);
        add(i<<1|1,mid+1,b,c);
    }        
}
__int64 buildtree(int loc,int l,int r)
{
	int mid;
	nodes[loc].l=l;nodes[loc].r=r;
	nodes[loc].inc=0;
	if(l==r)
		return nodes[loc].sum=num[l];
	else
	{
		mid=(l+r)>>1;		
		nodes[loc].sum=buildtree(loc<<1,l,mid)+buildtree(loc<<1|1,mid+1,r);
	}
	return nodes[loc].sum;
}

__int64 query(int loc,int l,int r)
{
	__int64 ans=0;
	if(nodes[loc].l==l && nodes[loc].r==r)
	{
		return nodes[loc].sum+(r-l+1)*nodes[loc].inc;
	}
	else
	{
		nodes[loc].sum+=(nodes[loc].r-nodes[loc].l+1)*nodes[loc].inc;
		int mid;
		mid=(nodes[loc].l+nodes[loc].r)>>1;
		add(loc<<1,nodes[loc].l,mid,nodes[loc].inc);//每次都只更新需要的区间,真妙~!
		add(loc<<1|1,mid+1,nodes[loc].r,nodes[loc].inc);
		nodes[loc].inc=0;
		if(r<=mid) ans=query(loc<<1,l,r);
		else if(l>mid) ans=query(loc<<1|1,l,r);
		else
		ans=query(loc<<1,l,mid)+query(loc<<1|1,mid+1,r);
	}
	return ans;
}
int main()
{
    int n,q;
    int i;
    int a,b,c;
    char ch;
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        for(i=1;i<=n;i++)  scanf("%d",&num[i]);
        buildtree(1,1,n);
        for(i=1;i<=q;i++)
        {
            cin>>ch;
            if(ch=='C')
            {
                scanf("%d%d%d",&a,&b,&c);
                add(1,a,b,c);
            }    
            else
            {
                scanf("%d%d",&a,&b);
                printf("%I64d\n",query(1,a,b));
            }    
        }    
    } 
    return 0;   
}     

POj 2528

离散化,剪枝

/*
数据离散化经典使用
这里注意离散化的时候不能因为怕浪费空间就用char,会溢出
离散化映射使用数组内容->数组下标
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define MM 10000000+200
#define maxn 20010
#define max(a,b)  (a)>(b)?(a):(b)
#define min(a,b)  (a)<(b)?(a):(b)
using namespace std;
struct node
{
	int l,r;
	int color;
}nodes[4*maxn];
int num[maxn];
int qmin,qmax;
int cnt;
int ans;
int a[maxn],b[maxn];
int vis[MM];
void insert(int l,int r,int color,int loc)//从区间关系入手
{
    if(nodes[loc].color==color) return;//颜色一致,不用更新
    if(r<nodes[loc].l || l>nodes[loc].r)
        return;//完全无覆盖
    if(nodes[loc].l>=l && nodes[loc].r<=r)
        {
                nodes[loc].color=color;//已经内含
                return;
        } //部分覆盖
    if(nodes[loc].color>=0)
	{
		nodes[loc<<1].color=nodes[loc<<1|1].color=nodes[loc].color;
		nodes[loc].color=-1;
	}
    //此节点为多色,先更新孩子的颜色,再为-1;
	insert(l,r,color,loc<<1);
	insert(l,r,color,loc<<1|1);
    /*int mid=(nodes[loc].l+nodes[loc].r)>>1;
    if(r<=mid) insert(l,r,color,loc<<1);
    else if(l>mid) insert(l,r,color,loc<<1|1);
    else
    {
        insert(l,mid,color,loc<<1);
        insert(mid+1,r,color,loc<<1|1);
    }*/
}
void buildtree(int loc,int l,int r)
{
	int mid;
	nodes[loc].l=l;nodes[loc].r=r;
	nodes[loc].color=0;
	if(l==r)
		return ;
	else
	{
		mid=(l+r)>>1;
		buildtree(loc<<1,l,mid);
		buildtree(loc<<1|1,mid+1,r);
	}
}

void dfs(int loc)
{
    if(nodes[loc].color==0)
        return;
    else if(nodes[loc].color>0 && vis[nodes[loc].color]==0)
        ans++,vis[nodes[loc].color]=1;
    else if(nodes[loc].color==-1)
    {
        
        dfs(loc<<1);
        dfs(loc<<1|1);
    }
}
int main()
{
    int n,q,i;
#ifndef ONLINE_JUDGE//提交的时候把这段删掉
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
#endif
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&q);
        cnt=0;ans=0;
        memset(vis,0,sizeof(vis));
        for( i=0;i<q;i++)
        {
            scanf("%d%d",a+i,b+i);
            if(vis[a[i]]==0)
                vis[a[i]]=1,num[cnt++]=a[i];
            if(vis[b[i]]==0)
                vis[b[i]]=1,num[cnt++]=b[i];
        }
        sort(num,num+cnt);
        int hash=0;
		
        for( i=0;i<cnt;i++)
            vis[num[i]]=++hash;//hash映射,
        buildtree(1,1,hash);
        for(i=0;i<q;i++)
            insert(vis[a[i]],vis[b[i]],i+1,1);
        memset(vis,0,sizeof(vis));
        dfs(1);
        printf("%d\n",ans);

    }
    return 0;
}


线段树入门(poj 3274 3468 2528)

标签:poj   线段树   二叉树   

原文地址:http://blog.csdn.net/gg_gogoing/article/details/38778637

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!