CF1335E2-Three Blocks Palindrome (hard version) (二分+双指针)
题目大意:
题意就是告诉你一个数n,然后给你n个数,每个数满足1<=a[i]<=200,让你求最大的子序列,子序列由x个a和y个b和x个a构成。
链接:https://codeforces.com/contest/1335/problem/E2
思路:
我们先用一个vector v记录1-200中每个数出现的位置,这里以1为例,v[1]中记录的是1这个数出现的所有位置,
然后我们用两个指针l(指向v[1]中的第一个数)和r(指向v[1]中的最后一个数),此时x=1,我们不难发现,1-200中的每个数,
只要出现在v[1][l]和v[1][r]之间均能满足所给条件,然后l++,r--,x++(x代表左边有x个1,右边有x个1),然后继续寻找,
不断的取最大值即可。复杂度为n*n*log(n),呸,转头一想好像不是这个复杂度(因为还要算上双指针移动,菜逼不知道咋算,
当时就想着试试感觉能过然后就过了),因为a[i]的最大值为200(即n的上限为200),所以能跑过去,不过好像是刚刚水过去。
可能我表述的有些混乱,看了代码应该可以稍微清晰一点。
代码:
#include <bits/stdc++.h> #define Pair pair<int,int> using namespace std; typedef long long ll; const int MAXN=5e5+5; const int INF=1e9; int a[MAXN]; vector<int>v[300]; int main() { ios::sync_with_stdio(false);cin.tie(0); int t;cin>>t; while(t--) { int n; cin>>n; for(int i=1; i<=200; i++) v[i].clear(); for(int i=1; i<=n; i++) { cin>>a[i]; v[a[i]].push_back(i); //记录每个a[i]出现的位置 } int max_=1; for(int i=1; i<=200; i++) { int ans=0; int l=0,r=v[i].size()-1; while(l<r) { //printf("%d %d %d\n",i,l,r); int item1=v[i][l],item2=v[i][r]; //printf("%d %d\n",item1,item2); ans+=2; //因为子序列左边添了一个,右边添了一个,所以此时子序列的长度加2 for(int j=1; j<=200; j++) { if(v[j].empty()) continue; if(j==i) { max_=max(max_,(int)v[i].size()); continue; } int index1=upper_bound(v[j].begin(),v[j].end(),item1)-v[j].begin(); int index2=upper_bound(v[j].begin(),v[j].end(),item2)-v[j].begin()-1; //index2-index1+1为中间的数的个数,ans+index2-index1+1即为子序列的大小 max_=max(max_,ans+index2-index1+1); } l++;r--; } } printf("%d\n",max_); } return 0; }