显然:
对于一个区间我们只需要负责处理最多次出现的那个元素
(我们把剩下的元素称为其他元素)
而最优策略是拿两个最多元素和一个其他元素组合成一个序列
故某个区间的答案是
m
a
x
(
最
多
元
素
个
数
−
其
他
元
素
,
1
)
max(最多元素个数-其他元素,1)
max(最多元素个数−其他元素,1)
所以对于单个区间,我们只需要求出其中出现次数最多的元素个数。
这个是好做的。
因为每次加上一个数或者删去一个数,区间出现次数最多元素的元素个数只会增加或者减少一,所以我们只需要用一个
c
n
t
[
i
]
cnt[i]
cnt[i]记录
i
i
i出现的次数,
r
n
k
[
i
]
rnk[i]
rnk[i]记录出现次数为
i
i
i次的元素个数。
每次维护
c
n
t
[
i
]
cnt[i]
cnt[i]和
r
n
k
[
i
]
rnk[i]
rnk[i]即可。(可以知道这个的复杂度是很低的,因为上文"出现次数最多元素的元素个数只会增加或者减少一")
同时因为询问是连续区间,所以想到莫队,复杂度可以控制在 O ( n n ) O(n\sqrt n) O(nn)以内。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e5+50;
int n,q;
int a[maxn],ans[maxn],rnk[maxn],cnt[maxn];
int nowl=1,nowr=0,nowans=0;
struct qu
{
int l,r,num;
}query[maxn];
bool cmp(qu a,qu b)
{
int t=sqrt(n);
if(a.r/t!=b.r/t)return a.r/t<b.r/t;
return a.l<b.l;
}
void add(int x)
{
if(cnt[a[x]]>0)rnk[cnt[a[x]]]--;
cnt[a[x]]++;
rnk[cnt[a[x]]]++;
nowans=max(nowans,cnt[a[x]]);
}
void del(int x)
{
rnk[cnt[a[x]]]--;
cnt[a[x]]--;
rnk[cnt[a[x]]]++;
while(rnk[nowans]==0)nowans--;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&query[i].l,&query[i].r);
query[i].num=i;
}
sort(query+1,query+1+q,cmp);
for(int i=1;i<=q;i++)
{
while(nowr<query[i].r)
{
nowr++;
add(nowr);
}
while(nowl>query[i].l)
{
nowl--;
add(nowl);
}
while(nowr>query[i].r)
{
del(nowr);
nowr--;
}
while(nowl<query[i].l)
{
del(nowl);
nowl++;
}
// cout<<nowans<<' '<<"aa"<<endl;
if(nowans<=(query[i].r-query[i].l+1+1)/2)ans[query[i].num]=1;
else ans[query[i].num]=max(1,nowans-(query[i].r-query[i].l+1-nowans));
}
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
return 0;
}
但就本题而言我们的做法可以有很多种,其中有两种觉得非常有借鉴意义。
1、如果有一个数在区间内出现了超过
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2次,那么它一定在某个子区间内出现超过
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2次(否则矛盾),所以我们只需要用线段树处理出每个区间超过
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2次的数,然后分别
c
h
e
c
k
check
check即可。
2、如果有一个数在区间内出现了超过
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2次,那么我们随机一个位置,随机到这个数的概率大于
1
/
2
1/2
1/2,所以我们随机
n
n
n次,
c
h
e
c
k
check
check每次取到的值,找到这个数的概率是非常大的。
注:check就简单二分下标~
同时顺便也学到了,对于出现次数大于
(
n
+
1
)
/
2
(n+1)/2
(n+1)/2的众数我们有摩尔投票算法可以
O
(
n
)
O(n)
O(n)求出…
b
y
t
h
e
w
a
y
by\,the\,way
bytheway我们还可以复习一下中位数的一些冷知识,对于一个区间我们可以先二分一个数,然后统计大于这个数和小于这个数的数的个数,来
c
h
e
c
k
check
check,复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。