https://codeforces.com/gym/102082/my
题意:给两个长度为N的序列,你的对手是第一个序列,你是第二个序列,你的对手的顺序不会改变,你现在可以改变你自己的顺序,要求在每个对应位置上你的数大于对手的数的数量最大的基础上,找一个最大的字典序。
分析:如果不考虑字典序的话,题目非常简单,只需要将两个数组排序然后两个下标模拟就可以了。但是要求字典序的话问题就比较复杂。首先我们可以很容易得到答案的长度,那么我们就可以根据这个长度去构造答案。
要求字典序最大的话,就要在每个位置上尽可能的取最大的数,很容易想到二分。即我选了某一个数后,剩下的数是否还能构成一个满足ans的排列,但是这里应该分两种情况。
(1)选取的数比对手的数大,满足线性关系(即大的数能够满足条件的话,那么小的数也一定可以满足条件,因为如果把较大的数放后面的话一定可以产生大于小的数所产生的贡献)。
(2)选取的数小于或等于对手的数,同上满足线性关系,但是这里一定不能将两种情况放在一起考虑(我有一个简洁明了的证明方法但是这里太小了写不下)。
那么我们优先对第一种情况进行二分,若没有找到再对第二种情况进行二分。
对对手的数组预排序可以让cheak函数到达的复杂度,那么总复杂度应该是。
#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;
}