Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 18520 | Accepted: 6687 |
Description
You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , ... , aj.
Input
The input consists of several test cases. Each test case starts with a line containing two integers n and q (1 ≤ n, q ≤ 100000). The next line contains n integers a1 , ... , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, ..., n}) separated by spaces. You can assume that for each i ∈ {1, ..., n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the
query.
The last test case is followed by a line containing a single 0.
Output
For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.
Sample Input
10 3 -1 -1 1 1 1 1 3 10 10 10 2 3 1 10 5 10 0
Sample Output
1 4 3
题意:给定一行有n个数的单调递增的数列,现在要求查询数列中某一段连续的子数列中出现最多的数字的出现次数。
思路:利用线段树,参照了hancks博主的方法,线段树的每个节点维护三个值,分别为:1:该节点对应的区间[l,r)中出现连续重复次数最多的数字的个数length,2:该区间的最左端点处的数字在区间中重复连续出现的次数left_num,3:该区间的最右端点处的数字在区间中重复连续出现的次数right_num。
三个值的更新方式如下:length默认取左右儿子length的最大值,若左右儿子之间存在交互,即左儿子的右端点和右儿子的左端点值相等,那么区间合并的时候length还可能是中间那一个数字出现的次数(num=right_num[l_child]+left_num[r_child]),那么length=max{length,num};
left_num的更新默认是继承左儿子的左端点处值得出现次数的left_num[l_child],接下来继续考虑左右儿子产生交互的会发生的情况,若合并之后发现原本的右儿子的左端点处的值和左儿子左端点处的一样,数列又是单调递增的,说明左儿子处的整条子数列的每一个值都是一样的,所以更新当前节点的
left_num=left_num[l_child]+left_num[r_child]
right_num的更新类同left_num;
线段树实现查询功能:查询的时候同样记录三个数,记查询区间为[a,b),那么递归的查询,每次查询[a,b)与线段树区间[l,r)的交集部分的length,left_num,right_num并记录之,更新方式同上,若无交集三个值都记为0,值得注意的是在[a,b)的两端处的left_num,right_num可能会是0(因为线段树的子区间可能与[a,b)无交集,
三个值都返回了0,而默认更新使得当前的left_num,right_num为0),但区间两端的更新错误并不会影响length的更新,所以不用担心。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> #include<vector> #include<set> using namespace std; const int ST_SIZE = (1 << 18) - 1,N_MAX=100000+2; int N, Q; int a[N_MAX]; int length[ST_SIZE], Left[ST_SIZE], Right[ST_SIZE]; struct result{//记录结果,长度,左,右端点开始相同值的个数 int length, right, left; result(int length,int right,int left):length(length),right(right),left(left) {}//!! }; void init(int k,int l,int r) { if (r - l == 1) { length[k] = Left[k] = Right[k] = 1; } else { int lch = k * 2 + 1; int rch = k * 2 + 2; init(lch, l, (l + r) / 2); init(rch, (l + r) / 2, r); length[k] = max(length[lch],length[rch]); Left[k] = Left[lch]; Right[k] = Right[rch]; int mid = (l + r) / 2; //接下来考虑左右儿子之间交互的关系 if (a[mid-1] == a[mid ]) {//左儿子的右端点等于右儿子左端点 length[k] = max(length[k],Left[rch]+Right[lch]); } if (a[l] == a[mid ]) {//左儿子的左端点等于右儿子的左端点 Left[k] += Left[rch]; } if (a[mid-1] == a[r - 1]) {//左儿子的右端点等于右儿子的右端点 Right[k] += Right[lch]; } } } result query(int k,int l,int r,int aa,int bb) {//函数返回的length:在[a,b)上出现次数最多的值的次数 if (bb <= l || aa >= r) { //left,right可能在端点处会出问题,但不影响length。。 return result(0,0,0); } else if (aa <= l&&bb >= r) { return result(length[k],Right[k],Left[k]); } else { int lch = k * 2 + 1; int rch = k * 2 + 2; int Mid = (l + r) / 2; result res1=query(lch, l, Mid,aa,bb); result res2=query(rch,Mid, r,aa,bb); int L_MAX = max(res1.length,res2.length); result res(L_MAX,res2.right,res1.left); if (a[Mid-1] == a[Mid]) {//左儿子的右端点等于右儿子左端点 res.length = max(res.length,res1.right+res2.left); } if (a[l] == a[Mid]) {//左儿子的左端点等于右儿子的左端点 res.left += res2.left; } if (a[Mid-1] == a[r - 1]) {//左儿子的右端点等于右儿子的右端点 res.right += res1.right; } return res; } } int main() { while (scanf(" %d", &N)!=EOF&&N) { scanf(" %d",&Q); for (int i = 0; i < N;i++) { scanf("%d",&a[i]); } init(0, 0, N); for (int i = 0; i < Q;i++) { int aa, bb; scanf("%d%d",&aa,&bb); aa--, bb--; result res = query(0,0,N,aa,bb+1); printf("%d ", res.length); } } return 0; }