题意简述
一个数组aaa中有n(n<=1e5)n(n<=1e5)n(n<=1e5)个数,a[i](a[i]<=1e9)a[i](a[i]<=1e9)a[i](a[i]<=1e9)表示第iii头牛的"血统编号"。请你帮Famer John删去其中所有的k(k<=n)k(k<=n)k(k<=n)种,使得剩下的牛血统相同的联通块长度最长。
数据
输入
第一行n,kn,kn,k
接着nnn行每行一个数,第iii行表示第iii头牛的"血统编号"
输出
rt。删掉kkk种牛后最长的相同血统联通块长度。
样例
输入
9 1
2
7
3
7
7
3
7
5
7
输出
4
解释
删去血统编号为3的所有牛,剩余是
2, 7, 7, 7, 7, 5, 7,其中有长度为4的相同血统联通块(第222个~第555个)
思路
我们会发现,如果一段区间里有k+1k+1k+1种牛,我们删去kkk种,剩下的就只有一种了。
这个题,1e51e51e5的数据,(伟大的班长lyz:显然二进制枚举珂以过!),肯定不能二进制枚举吧。。。想个线性的枚举办法。
我们现在强人锁♂男规定我们以rrr为区间右端点,并且必须选上a[r]a[r]a[r]作为剩下的那一种牛,然后找一个最远的左端点lll使得区间中有恰好kkk种牛。此时的答案就是区间中a[r]a[r]a[r]这种牛在[l,r][l,r][l,r]出现的次数,这个答案求一下maxmaxmax就好了。有没有漏考虑情况呢?好像没有。。。乍一想,好像就O(n2)O(n^2)O(n2)了,虽然比lyzlyzlyz的二进制枚举快0x3f3f3f3f0x3f3f3f3f0x3f3f3f3f倍,但是还是不能过。。。并且只能过一个点,和二进制枚举的分一样高。。。
(lyz:Oh,yeah♂)
我们来想想如何 搞♂死lyz 优化。我们会发现,随着rrr的不断右移,种类也就不会减少,如果l想要保持区间恰好还剩下k+1种牛,就不会向左移动\color{red}种类也就不会减少,如果l想要保持区间恰好还剩下k+1种牛,就不会向左\\移动种类也就不会减少,如果l想要保持区间恰好还剩下k+1种牛,就不会向左移动。显然。这样就有了一个单调性,脑海中第一个浮现的便是活泼可爱美丽的尺取法了。
还有一个问题:如何统计每一个种类出现了多少次呢?暴力开数组。。。1e91e91e9珂存不下啊!
其实,我们只要给每个种类分配一个编号就珂以了。我们并不需要知道一个牛的血统编号是多少,只要把不同血统的牛编号不同,相同血统的牛血统编号相同即可。所以就按照输入顺序编上新编号,如果出现过就按照编好的号赋值,如果没有出现过就编上一个新的号。这个过程用STLSTLSTL的福利map<>map<>map<>实现。
STL,一时用一时爽,一直用一直爽
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 1001000
map<int,int>mp;
//重新编号
int n,k,a[N];
void Input()
{
int tmp=0;//当前用的编号
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (!mp[a[i]]) mp[a[i]]=++tmp;//没有出现过就新编一个号
a[i]=mp[a[i]];//使用新编号
}
}
int cnt[N];
//由于新编的号都<=n,所以用数组就珂以存下当前每个编号选了多少个
void Solve()
{
int type=0;
//选了多少编号
int l=1,r=0;
int ans=-1;
while(l<=n and r<=n)
{
++r;
if (cnt[a[r]]==0) ++type;
//如果是第一次出现,那么就相当于多选了一个类型,要++type
++cnt[a[r]];//这个一定是要加的,无疑问
while(type>k+1)//必须要保证恰好选k+1个
{
--cnt[a[l]];//去除现在的l
if (cnt[a[l]]==0) --type;//如果没减没了,type也要--
++l;//l右移
}
ans=max(ans,cnt[a[r]]);//由于我们要强♂制选a[r]这个种类,所以要用cnt[a[r]]更新答案
}
printf("%d\n",ans);
}
void Main()
{
Input();
Solve();
}
};
main()
{
Flandle_Scarlet::Main();
return 0;
}