E题
给
你
一
个
长
度
为
n
的
数
组
,
最
多
删
除
k
个
元
素
,
求
最
长
相
同
连
续
子
序
列
。
给你一个长度为n的数组,最多删除k个元素,求最长相同连续子序列。
给你一个长度为n的数组,最多删除k个元素,求最长相同连续子序列。
0
<
=
k
<
=
n
<
=
1
∗
1
0
5
1
<
=
a
[
i
]
<
=
1
∗
1
0
9
0<=k<=n<=1*10^{5} \quad \quad 1<=a[i]<=1*10^{9}
0<=k<=n<=1∗1051<=a[i]<=1∗109
本
题
由
于
数
据
范
围
是
1
e
5
,
所
以
肯
定
是
本题由于数据范围是1e5,所以肯定是
本题由于数据范围是1e5,所以肯定是nlogn
的
做
法
的做法
的做法
考
虑
到
最
长
相
同
子
序
列
肯
定
是
同
一
种
元
素
构
成
的
考虑到最长相同子序列肯定是同一种元素构成的
考虑到最长相同子序列肯定是同一种元素构成的
所
以
我
们
可
以
对
每
个
元
素
检
验
可
构
成
的
最
长
连
续
子
序
列
。
所以我们可以对每个元素检验可构成的最长连续子序列。
所以我们可以对每个元素检验可构成的最长连续子序列。
我
们
可
以
枚
举
右
端
点
,
然
后
二
分
左
端
点
,
如
果
删
除
k
个
点
能
达
到
长
度
为
l
我们可以枚举右端点,然后二分左端点,如果删除k个点能达到长度为l
我们可以枚举右端点,然后二分左端点,如果删除k个点能达到长度为l,
删
除
k
个
点
肯
定
能
达
到
删除k个点肯定能达到
删除k个点肯定能达到l’<l
,
所
以
答
案
是
可
以
二
分
的
,
,所以答案是可以二分的,
,所以答案是可以二分的,
所
以
我
们
只
要
枚
举
右
端
点
+
验
证
就
好
了
,
所以我们只要枚举右端点+验证就好了,
所以我们只要枚举右端点+验证就好了,
要
提
前
预
处
理
每
个
数
到
达
某
个
位
置
的
总
个
数
,
要提前预处理每个数到达某个位置的总个数,
要提前预处理每个数到达某个位置的总个数,
二
分
条
件
为
(
l
−
r
之
间
的
a
[
i
]
的
个
数
+
k
)
>
=
(
r
−
l
+
1
)
.
二分条件为(l-r之间的a[i]的个数+k)>=(r-l+1).
二分条件为(l−r之间的a[i]的个数+k)>=(r−l+1).
E题代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int maxn = 1e5+5;
#define dbg(x) cout<<#x<<" :"<<x<<endl;
int a[maxn];
map<int,vector<int> > mm;
map<int,int> pre;
map<pair<int,int>,int> sum;
int main()
{
int n,k;
int ans=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++) mm[a[i]].push_back(i);
for(int i=1;i<=n;i++)
{
sum[pair<int,int>(a[i],i)]=sum[pair<int,int>(a[i],pre[a[i]])]+1;
pre[a[i]]=i;
}
map<int,vector<int> >::iterator it;
for(it=mm.begin();it!=mm.end();++it)
{
int tmp=it->first;
int sz=(it->second).size();
if(sz<=1) continue;
for(int i=0;i<sz;i++)
{
int l=0,r=i,mid;
int rr=mm[tmp][i];
while(l<=r)
{
mid=(l+r)>>1;
if((rr-mm[tmp][mid]+1)-(sum[pair<int,int>(tmp,rr)]-sum[pair<int,int>(tmp,mm[tmp][mid])]+1)<=k) r=mid-1;
else l=mid+1;
}
ans=max(ans,sum[pair<int,int>(tmp,mm[tmp][i])]-sum[pair<int,int>(tmp,mm[tmp][l])]+1);
}
}
printf("%d\n",ans);
return 0;
}