给定一个数组,找出最长的子序列,满足 |
− − − − − − − − − − − − − − − − − − − − − 华 丽 分 割 线 w ( ゚ Д ゚ ) w − − − − − − − − − − − − − − − − − − − − − − − − \color{Red}{---------------------华丽分割线w(゚Д゚)w------------------------} −−−−−−−−−−−−−−−−−−−−−华丽分割线w(゚Д゚)w−−−−−−−−−−−−−−−−−−−−−−−−
看到这数据,就觉得暴力无疑。
三 种 情 况 三种情况 三种情况
Ⅰ . 当 x = 0 , 也 就 是 只 有 中 间 部 分 , 答 案 就 是 出 现 次 数 最 多 的 那 个 数 字 。 Ⅰ.当x=0,也就是只有中间部分,答案就是出现次数最多的那个数字。 Ⅰ.当x=0,也就是只有中间部分,答案就是出现次数最多的那个数字。
Ⅱ . 当 y = 0 , 也 就 是 只 有 两 边 的 部 分 , 那 就 要 枚 举 前 半 部 分 区 间 和 后 半 部 分 区 间 , 设 区 间 为 [ 1 , L ] 和 [ R , n ] Ⅱ.当y=0,也就是只有两边的部分,那就要枚举前半部分区间和后半部分区间,设区间为[1,L]和[R,n] Ⅱ.当y=0,也就是只有两边的部分,那就要枚举前半部分区间和后半部分区间,设区间为[1,L]和[R,n]
再 枚 举 a 的 取 值 ( 1 − 26 ) , 答 案 是 区 间 [ 1 , L ] a 出 现 次 数 和 [ R , n ] a 出 现 次 数 的 较 小 值 再枚举a的取值(1-26),答案是区间[1,L]a出现次数和[R,n]a出现次数的较小值 再枚举a的取值(1−26),答案是区间[1,L]a出现次数和[R,n]a出现次数的较小值。
Ⅲ . 当 x 和 y 都 不 为 0 , 怎 么 办 ? 其 实 和 2 差 不 多 , 只 不 过 现 在 因 为 y 的 存 在 , 在 区 间 [ L , R ] 要 计 算 最 大 的 y , 也 就 是 出 现 次 数 最 多 的 。 Ⅲ.当x和y都不为0,怎么办?其实和2差不多,只不过现在因为y的存在,在区间[L,R]要计算最大的y,也就是出现次数最多的。 Ⅲ.当x和y都不为0,怎么办?其实和2差不多,只不过现在因为y的存在,在区间[L,R]要计算最大的y,也就是出现次数最多的。
具体实现,需要维护几个数组降低复杂度,具体看代码。
但 是 上 面 的 方 法 对 于 这 题 的 h a r d 版 本 n = 2 e 5 实 在 不 够 看 , 所 以 有 了 下 面 的 方 法 。 但是上面的方法对于这题的hard版本n=2e5实在不够看,所以有了下面的方法。 但是上面的方法对于这题的hard版本n=2e5实在不够看,所以有了下面的方法。
− − − − − − − − − − − − − − − − − − − − − O ( ∩ ∩ ) O 分 割 分 割 分 割 − − − − − − − − − − − − − − − − − − − − − − − − \color{Orange}{---------------------O(∩_∩)O分割分割分割------------------------} −−−−−−−−−−−−−−−−−−−−−O(∩∩)O分割分割分割−−−−−−−−−−−−−−−−−−−−−−−−
这种数据,枚举区间的办法是行不通了,只有 a [ i ] < = 200 & & a [ i ] > = 1 a[i]<=200\&\&a[i]>=1 a[i]<=200&&a[i]>=1这个范围还算友好
我们可以枚举数字作为a。
比如3出现了5次,可行的枚举就是左右各一次,左右各两次。
在这个基础上,要使空出来的区间最大(因为可能要放b),显然应该选最左边的几个和最右边的几个。
那 我 们 开 个 v e c t o r 装 每 个 数 字 出 现 的 位 置 , 枚 举 就 是 了 。 那我们开个vector装每个数字出现的位置,枚举就是了。 那我们开个vector装每个数字出现的位置,枚举就是了。
那 空 出 来 的 区 间 呢 ? 如 何 快 速 知 道 哪 个 数 在 区 间 次 数 最 多 作 为 b ? 那空出来的区间呢?如何快速知道哪个数在区间次数最多作为b? 那空出来的区间呢?如何快速知道哪个数在区间次数最多作为b?
其 实 在 枚 举 的 过 程 中 , 空 出 来 的 区 间 总 是 连 续 减 少 的 , 所 以 可 以 动 态 维 护 1 − 200 的 出 现 次 数 其实在枚举的过程中,空出来的区间总是连续减少的,所以可以动态维护1-200的出现次数 其实在枚举的过程中,空出来的区间总是连续减少的,所以可以动态维护1−200的出现次数
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+9;
vector<int>vec[209];
int a[maxn],t,n,vis[201];
int main()
{
cin>>t;
while(t--)
{
for(int i=1;i<=200;i++) vec[i].clear();
cin>>n;
int ans=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
vec[a[i]].push_back(i);
}
//处理x为0
for(int i=1;i<=200;i++)
{
int tt=vec[i].size();
ans=max(ans,tt);
}
//处理两头夹中间
for(int i=1;i<=200;i++)
{
int k=vec[i].size(),temp=0;
if(k<2) continue;
memset(vis,0,sizeof(vis));
int l=vec[i][0]+1,r=vec[i][k-1]-1;
for(int j=l;j<=r;j++) vis[a[j]]++;//预处理拿1个的时候
for(int j=1;j<=200;j++) temp=max(temp,vis[a[j]]);
ans=max(ans,2+temp);
for(int j=2;j<=k/2;j++)//左边拿j,右边拿j
{
int l=vec[i][j-1]+1,r=vec[i][k-j]-1;
for(int q=vec[i][j-2]+1;q<l;q++) vis[a[q]]--;
for(int q=r+1;q<=vec[i][k-j+1]-1;q++) vis[a[q]]--;
temp=0;
for(int q=1;q<=200;q++)
ans=max(ans,vis[q]+j*2);
}
}
cout<<ans<<endl;
}
}