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