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

poj 3368 Frequent values(离散化+线段树区间求最值)

2014年04月05日 ⁄ 综合 ⁄ 共 1951字 ⁄ 字号 评论关闭

http://poj.org/problem?id=3368

题意:

给定一个非递减序列和若干个询问,序列中的数有可能是相等的,每个询问输入l,r,问在区间[l,r]相等的数中所含个数最多的是多少?


比如 -1 -1 1 1 1 1 3 10 10 10 ,若询问[1, 10],则答案应该是4,因为在区间[1,10]中连续相等的数中个数最多的是1,有4个。

思考加发呆有半个小时,也没想出来用线段树还怎么解决。开始我想简单了,给出序列的长度n,我就想直接建树1~n,根据非递减性区间求最值。。但这样面临着一个巨大的问题,就是当两个子区间合并成一个区间时,比如实例中当[1,5]和[6,10]合并成[1,10]时,我只记录了[1,5]的最值为3(1的个数最多)和[6,10]的最值也为3(10的个数最多),但此时a[5]与a[6]相等,如果直接合并[1,10]的最值就是3,但显然这是错误的。不知不觉啰嗦一坨,还是错的。。。其实原序列经过离散以后再用上面的方法做就对了。

所谓离散就是把连续相等的点缩成一个点,同时用结构体记录该点所代表的的区间的端点。具体见代码。

 比如:
-1   -1   1   1   1   1   3   10   10   10     把原序列离散成了4个点。

 
 1    1   2   2   2   2   3    4     4     4

这样1~4建树就可以了,每个节点增加一个域maxnum表示该区间内的最值,也就是原序列中相同的数的个数最大值。


#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int maxn = 100005;

int a[maxn];
int hash[maxn];
int p;

struct node
{
	int start;
	int end;
}seg[maxn];//seg记录每个离散得到的点对应区间的端点。

struct line
{
	int l;
	int r;
	int maxnum;
}tree[maxn<<2];

//建树
void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	if(l == r)
	{
		int k = l;
		tree[v].maxnum = seg[k].end - seg[k].start + 1;
		return;
	}

	int mid = (tree[v].l+tree[v].r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
	tree[v].maxnum = max(tree[v*2].maxnum, tree[v*2+1].maxnum);
}

//区间求最值
int query(int v, int l, int r)
{
	if(tree[v].l == l && tree[v].r == r)
		return tree[v].maxnum;

	int mid = (tree[v].l+tree[v].r)>>1;
	if(r <= mid)
		return query(v*2,l,r);
	else
	{
		if(l > mid)
			return query(v*2+1,l,r);
		else
			return max(query(v*2,l,mid),query(v*2+1,mid+1,r));
	}
}

int main()
{
	int n,m;
	int pre;
	while(~scanf("%d",&n))
	{
		if(n == 0) break;
		scanf("%d",&m);
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);

		pre = 100001;
		p = 0;		//p为离散成的点。
		for(int i = 1; i <= n; i++)
		{
			if(a[i] != pre)
			{
				pre = a[i];
				p++;
				seg[p].start = i;
				seg[p].end = i;
			}
			else
				seg[p].end = i;
			hash[i] = p;	//标记原序列中每个下标对应离散后的点。
		}

		build(1,1,p);	//对离散后的点建树

		while(m--)
		{
			int x,y,xx,yy;
			scanf("%d %d",&x,&y);

			xx = hash[x];//下标为x的点离散后对应的点
			yy = hash[y];//下标为y的点离散后对应的点

			if(xx == yy)//离散后的点是用一个点,说明[x,y]为同一个点集
			{
				printf("%d\n",y-x+1);
				continue;
			}

			else		//[x,y]不是同一个点集,就分为三部分,第二部分的点集最大值用线段树来求。
			{
				int ans1 = seg[xx].end-x+1;
				int ans2 = 0;
				int ans3 = y-seg[yy].start+1;
				if(yy-xx > 1)
					ans2 = query(1,xx+1,yy-1);
				printf("%d\n",max( max(ans1,ans2),ans3 ) );
				continue;
			}
		}
	}
	return 0;
}


抱歉!评论已关闭.