题意:给你一个长度为n的,非递减的数组,现在有q次询问,每次询问输出[l, r]中出现次数最多的那个数的出现次
数。(n, q, |a[i]| <= 1e5)
思路:第一反应是主席树或者莫队搞,写了发莫队n*sqrt(n)*log(n), 6e8,T了。。。注意给你的序列的是非递减的。所以我们可以将相同的数合并为1块,如1 1 1 2 2可以表示成(1, 3), (2, 2)两块,表示1有3个,2有2个。现在可以开这些数组,id[i]表示第i个数在第几块,len[i]表示第i块的长度,lef[i]表示第i块的左端点,rig[i]表示第i块的右端点。
对于每次询问要求的就是下面三者中最大的
1.从L到L所在的块的右端点的元素个数:rig[L]-L+1
2.从R到R所在的段的左端点的元素个数:R-lef[R]+1
3.中间第num[L]+1段到第num[R]-1段的cnt的最大值(RMQ)
对于1, 2先特判L, R是否在一个块内,如在一个块内直接是R-L+1.这样保证了左右端点一定在询问区间内。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn], id[maxn], lef[maxn], rig[maxn], len[maxn];
int n, q, cnt;
int mm[maxn], dpMax[maxn][20];
void initRMQ()
{
for(int i = 1; i <= cnt; i++)
dpMax[i][0] = len[i];
for(int j = 1; j <= mm[n]; j++)
for(int i = 1; i+(1<<j)-1 <= n; i++)
dpMax[i][j] = max(dpMax[i][j-1], dpMax[i+(1<<(j-1))][j-1]);
}
int RMQ(int l, int r)
{
int k = mm[r-l+1];
return max(dpMax[l][k], dpMax[r-(1<<k)+1][k]);
}
int main(void)
{
mm[0] = -1;
for(int i = 1; i < maxn; i++)
mm[i] = ((i&(i-1))==0) ? mm[i-1]+1 : mm[i-1];
while(cin >> n, n)
{
scanf("%d", &q);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
cnt = 1;
id[1] = 1;
lef[1] = 1;
rig[1] = 1;
len[1] = 1;
for(int i = 2; i <= n; i++)
{
if(a[i] == a[i-1])
{
id[i] = id[i-1];
len[id[i]]++;
rig[id[i]] = i;
}
else
{
id[i] = ++cnt;
lef[id[i]] = i;
rig[id[i]] = i;
len[id[i]] = 1;
}
}
initRMQ();
while(q--)
{
int l, r;
scanf("%d%d", &l, &r);
if(id[l] == id[r])
printf("%d\n", r-l+1);
else
{
int ans = 0;
if(id[l]+1 <= id[r]-1)
ans = RMQ(id[l]+1, id[r]-1);
ans = max(ans, max(rig[id[l]]-l+1, r-lef[id[r]]+1));
printf("%d\n", ans);
}
}
}
return 0;
}