题意:一个具有n(1~100000)个点的序列,从大到小排序,有一些点的大小是相等的,问区间[a,b]上,相等大小的点最多是几个。
思路:线段树+离散化。离散的运用:将连续的相等的一个区间的点集,聚集为线段树的一个节点,并且在这个节点上有信息能体现出这个区间的性质。
代码如下:
#include<iostream> using namespace std; const int Max = 100050; struct { int l, r, max; }node[3*Max]; struct { int sta, end; }seg[Max]; int num[Max], hash[Max]; int max(int a, int b) { return a > b ? a : b; } void BuildTree(int left, int right, int u) { // 建树。 node[u].l = left; node[u].r = right; if(left == right) { int k = left; node[u].max = seg[k].end - seg[k].sta + 1; // max保存这个点集的点的数量。 return; } int mid = (left+right)>>1; BuildTree(left, mid, u<<1); BuildTree(mid+1, right, (u<<1)+1); node[u].max = max(node[u<<1].max, node[(u<<1)+1].max); } int query(int left, int right, int u) { // 查询。 if(node[u].l == left && node[u].r == right) return node[u].max; if(right <= node[u<<1].r) return query(left, right, u<<1); if(left >= node[(u<<1)+1].l) return query(left, right, (u<<1)+1); int a = query(left, node[u<<1].r, u<<1); int b = query(node[(u<<1)+1].l, right, (u<<1)+1); return max(a, b); } int main() { int n, m, i; while(scanf("%d", &n) && n) { scanf("%d", &m); for(i = 1; i <= n; i ++) scanf("%d", &num[i]); int k = 0, pre = 999999; for(i = 1; i <= n; i ++) { // 离散化,将点的序列离散化为一个个的点集。 if(num[i] != pre) { pre = num[i]; k++; seg[k].sta = i; seg[k].end = i; } else seg[k].end = i; hash[i] = k; // hash保存序列上第i个点,对应的点集的下标。 } BuildTree(1, k, 1); while(m--) { int a, b, pos1, pos2; scanf("%d%d", &a, &b); pos1 = hash[a]; pos2 = hash[b]; // *.如下,好的主函数操作能很大程度上简化线段树的复杂度。 if(pos1 == pos2) // 情况1:[a,b]为一个点集。 printf("%d\n", b-a+1); else { // 情况2,3:[a,b]包含多个点集。 int n1 = seg[pos1].end - a + 1; int n2 = 0; int n3 = b - seg[pos2].sta + 1; if(pos2 - pos1 > 1) // 情况3:[a,b]包含3个以上点集,中间点集最大值用到线段树。 n2 = query(pos1+1, pos2-1, 1); printf("%d\n", max(max(n1, n3), n2)); } } } return 0; }