GYM 102082 K Sixth Sense【瞎**二分】

https://codeforces.com/gym/102082/my

题意:给两个长度为N的序列,你的对手是第一个序列,你是第二个序列,你的对手的顺序不会改变,你现在可以改变你自己的顺序,要求在每个对应位置上你的数大于对手的数的数量最大的基础上,找一个最大的字典序。

分析:如果不考虑字典序的话,题目非常简单,只需要将两个数组排序然后两个下标模拟就可以了。但是要求字典序的话问题就比较复杂。首先我们可以很容易得到答案的长度,那么我们就可以根据这个长度去构造答案。

要求字典序最大的话,就要在每个位置上尽可能的取最大的数,很容易想到二分。即我选了某一个数后,剩下的数是否还能构成一个满足ans的排列,但是这里应该分两种情况。

(1)选取的数比对手的数大,满足线性关系(即大的数能够满足条件的话,那么小的数也一定可以满足条件,因为如果把较大的数放后面的话一定可以产生大于小的数所产生的贡献)。

(2)选取的数小于或等于对手的数,同上满足线性关系,但是这里一定不能将两种情况放在一起考虑(我有一个简洁明了的证明方法但是这里太小了写不下)。

那么我们优先对第一种情况进行二分,若没有找到再对第二种情况进行二分。

对对手的数组预排序可以让cheak函数到达O(N)的复杂度,那么总复杂度应该是O(n^2logn)

#include "bits/stdc++.h"

namespace fastIO {
#define BUF_SIZE 100000
    bool IOerror = 0;

    inline char nc() {
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
        if (p1 == pend) {
            p1 = buf;
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);
            if (pend == p1) {
                IOerror = 1;
                return -1;
            }
        }
        return *p1++;
    }

    inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }

    inline void read(int &x) {
        char ch;
        while (blank(ch = nc()));
        if (IOerror) return;
        for (x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
    }

    inline void Out(int a) {
        if (a < 0) {
            putchar('-');
            a = -a;
        }
        if (a >= 10) {
            Out(a / 10);
        }
        putchar(a % 10 + '0');
    }

#undef BUF_SIZE
};
using namespace fastIO;
using namespace std;

int n;
int a[5004];//预排序数组
int aa[5004];
bool vis[5004];//标记a数组中的是否已经用过
vector<int> b;
int win;//记录剩下的还应得到的积分

int getwin() {
    memset(vis, 0, sizeof(vis));
    int pos = 0;
    for (int i = 0; i < n; ++i) {
        if (b[i] > a[pos])pos++;
    }
    return pos;
}

bool che(int mid, int p, int ok) {
    int pos = 0;
    int res = 0;
    for (int i = 0; i < b.size(); ++i) {
        while (pos == ok || vis[pos])pos++;
        if (i == mid)continue;
        if (b[i] > a[pos]) {
            res++;
            pos++;
        }
    }
    res += b[mid] > aa[p];
    return res >= win;
}

int main() {
    //read(n);
    cin>>n;
    int x;
    for (int i = 0; i < n; ++i) {
        //read(a[i]);
        scanf("%d",&a[i]);
        aa[i] = a[i];
    }
    for (int i = 0; i < n; ++i) {
        //read(x);
        scanf("%d",&x);
        b.push_back(x);
    }
    int T=clock();
    sort(a, a + n);
    sort(b.begin(), b.end());
    win = getwin();
    vector<int> ans;
    for (int i = 0; i < n; ++i) {
        int pos = lower_bound(a, a + n, aa[i]) - a;
        while (vis[pos])pos++;
        int l = upper_bound(b.begin(), b.end(), aa[i]) - b.begin(), r = b.size() - 1;
        int ANS = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (che(mid, i, pos)) {
                ANS = mid;
                l = mid + 1;
            } else r = mid - 1;
        }
        if (ANS == -1) {
            l = 0, r = upper_bound(b.begin(), b.end(), aa[i]) - b.begin() - 1;
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (che(mid, i, -1)) {
                    ANS = mid;
                    l = mid + 1;
                } else r = mid - 1;
            }
        }
        ans.push_back(b[ANS]);
        win -= b[ANS] > aa[i];
        b.erase(b.begin() + ANS);

        vis[pos] = 1;
    }
    for (int i = 0; i < ans.size(); ++i) {
        //Out(ans[i]);putchar(' ');
        printf("%d ", ans[i]);
    }
    puts("");
    //cout<<clock()-T<<endl;
}

 

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值