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;
}