Educational Codeforces Round 100 (Rated for Div. 2)D. Pairs(选定取min剩余取max组成给定集合)

题:https://codeforces.com/contest/1463/problem/D

题意:从1~2*n之间随意取出n个pair,选定其中x个取出其中的最小值,选定其中(n-x)取出其中的最大值,组成n个数恰好是给定的set(这里记为f1数组),问0<=x<=n,有多少个取值是满足题意的(n<=2e5)。

分析:

  • 单单考虑取最小值,看最大能取到多少个pair使得这些最小值都在set中;
  • 设f2数组为没有出现在set中的数(f1和f2数组升序),那么最大能取到的就是f1的前段和f2的后段组成pair,且具有单调性,那么可以用二分求出最大值记为maxL;
  • 取最大值同理,记为minR,那么合法的x就是在[n-minR+1,maxL]之间取(因为要保证选定pair取min的同时剩余的pair要取max最终能组成题目给的set),答案就是这个区间长度。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define MP make_pair
#define UM unordered_map
typedef long long ll;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll INF=1e18;
#define pi 3.1415926535898
#define DEC (pi/180)
const static int M=4e5+5;
int vis[M],f1[M<<1],f2[M<<1];
void pn(){
    puts("NO");
}
void py(){
    puts("YES");
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=2*n;i++) vis[i]=0;
        for(int x,i=1;i<=n;i++){
            scanf("%d",&x);
            vis[x]=1;
        }
        int tot1=0,tot2=0;
        for(int i=1;i<=2*n;i++){
            if(vis[i]==1)
                f1[++tot1]=i;
            else
                f2[++tot2]=i;
        }
        int L=0,R=n,maxL=0;
        while(L<=R){
            int midd=(L+R)>>1;
            int ok=1;
            for(int i=1;i<=midd;i++) if(f1[i]>f2[n-midd+i]) ok=0;///要最小值为标记过的值
            if(ok){
                L=midd+1;
                maxL=midd;
            }
            else R=midd-1;
        }
        L=0,R=n;
        int minR=n;
        while(L<=R){
            int midd=(L+R)>>1;
            int ok=1;
            for(int i=1;i<=midd;i++) if(f2[i]>f1[n-midd+i]) ok=0;///要最大值为标记过的值
            if(ok){
                L=midd+1;
                minR=midd;
            }
            else R=midd-1;
        }
        printf("%d
",maxL-(n-minR)+1);///相交区间的长度
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/14155339.html