CF1408H Rainbow Triples
请叫我钢铁战神。但是这题实在简单啊,真怪。
首先考虑二分答案,答案显然是有可二分性的。若答案为
考虑去掉这个二分答案。首先我们发现无论二分的答案是多少,每个数能匹配的前缀和后缀不会变。也就是说,我能填完的前缀以及能填后缀的备用集合不会变。那我岂不是剩下的后缀越长越好,以免有剩下的。于是直接看在最多的情况下能匹配对少就行。
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int mod=1e9+7;
#define inf 1e9
#define pb push_back
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,T,L[maxn],R[maxn],a[maxn],tpl,tpr,ans;
vector<int>P[maxn];
priority_queue<int>I,O;
inline void solve(){
n=read();m=tpl=tpr=ans=0;
for(int i=1;i<=n;i++)
a[i]=read(),m+=!a[i];
m/=2;if(!m)return puts("0"),void();
for(int i=1;i<=n;i++)L[i]=R[i]=0;
for(int i=1;i<=n;i++)
if(!a[i]){++tpl;if(tpl>m)break;}
else L[a[i]]=tpl;
for(int i=n;i>=1;i--)
if(!a[i]){++tpr;if(tpr>m)break;}
else R[a[i]]=tpr;
for(int i=0;i<=m;i++)P[i].clear();
for(int i=1;i<=n;i++)
if(L[i]||R[i])P[L[i]].pb(R[i]);
while(!I.empty())I.pop();
while(!O.empty())O.pop();
for(auto x:P[0])O.push(-x);
for(int i=1;i<=m;i++)
for(auto x:P[i]){
if(ans<i)I.push(x),++ans;
else I.push(x),O.push(-I.top()),I.pop();
}int dt=1;
while(!O.empty()&&ans+dt<=m){
int x=-O.top();O.pop();
if(x>=dt)++dt;
}printf("%d\n",ans+dt-1);
}
int main(){
T=read();
while(T--)solve();
return 0;
}
深深地感到自己的弱小。