题目链接:https://codeforces.com/contest/1463/problem/D
题目大意
给出一个序列,长度为n,现在支持有两种操作:
1:对于一组pair(x,y)选择pair中小的数留下
2:对于一组pair(x,y)选择pair中大的数留下
现在有1-2*n的数,数值分别为1-2*n,现在可以将这2*n个数进行任意的分组,对其中的x组执行1操作,n-x组执行2操作
输出有多少个x可以使得,执行完操作以后,得到给出的数组n
题目思路
挺卡的…
大概是自己菜了…
首先将题目给出的与未给出的分成两组
下面肯定两组之间进行匹配,那么贪心的去考虑:
1.给出的序列要成为最小值的x个,绝对是最小的x个,也就是前x个,并且这x个应该去匹配最大的未匹配的x个
2.同理,给出序列的[x+1,n]个应该去匹配未匹配的[1,n-x]个
这样就可以保证如果不可以匹配成功,那么一定不可以匹配成功
从这里也可以看出,这两者满足单调性.
所以这个应该是个区间问题.
所以只需要二分一下上边界、与下边界就可以了
至于为什么存在单调性,可以思考一下代码里的judge函数,或者上面的匹配规则.
Code:
/*** keep hungry and calm CoolGuang! ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 4e5+700;
const int mod= 1e9+7;
const int up = 1e9;
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll num[maxn];
int vis[maxn],b[maxn];
int judge(int x){
for(int i=1;i<=x;i++)
if(num[i]>b[n-x+i]) return 1;///x大了
for(int i=x+1;i<=n;i++)
if(num[i]<b[i-x]) return 2;///x太小了
return 0;
}
int main(){
int T;scanf("%d",&T);
while(T--){
read(n);
int cnt = 0;
for(int i=1;i<=2*n;i++) vis[i] = 0;
for(int i=1;i<=n;i++){
read(num[i]);
vis[num[i]] = 1;
}
for(int i=1;i<=2*n;i++){
if(vis[i]) continue;
b[++cnt] = i;
}
int l = 0,r = n,lbound = 0;
while(l<=r){
int mid = (l+r)/2;
if(judge(mid) != 2){
lbound = mid;
r = mid - 1;
}else l = mid+1;
}
l = 0,r = n;
int ans = 0;
while(l<=r){
int mid = (l+r)/2;
if(judge(mid) != 1){
ans = max(ans,mid-lbound+1);
l = mid + 1;
}else r = mid-1;
}
printf("%d\n",ans);
}
return 0;
}
/***
10
0101011101
****/