决斗(模拟赛题目T3)分析
题目概述
田忌赛马的最大字典序。
分析
感觉很典,所以就记下来了。
我们考虑一个非常重要的事实:
- 田忌赛马对于每一个 \(a_i\) 找的是第一个比他大的 \(b_i\)。
- 而字典序最大又需要前面的尽可能大。
这似乎产生了矛盾,让这道题目看起来有点难。
我们考虑不用 multiset 或者双指针求这个值,我们考虑分治的过程。
CDQ 分治是这样的:
对于左区间算好其分内的答案,右区间算好其分内的答案。
要合并区间的时候,左区间和右区间匹配就行了。
那么我们只需要记录每个区间剩下多少个 \(a\) 中的元素和 \(b\) 中的元素还没有匹配即可。
最精华的部分来了:由于我们对于每一个 \(a_i\) 肯定进行二分答案是否可行,因为字典序本质上就有一个贪心的过程。我们考虑把这个思想搬到一个可支持修改的线段树上面即可。
在线段树上面这个是好维护的,于是这道题就做完了。
代码
时间复杂度 \(\mathcal{O}(n\log V\log n)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <set>
#define int long long
#define N 100005
using namespace std;
struct node{
int val,a,b;
}tr[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
void pushup(int x) {
int res = min(tr[ls(x)].a,tr[rs(x)].b);
tr[x].a = tr[ls(x)].a + tr[rs(x)].a - res;
tr[x].b = tr[ls(x)].b + tr[rs(x)].b - res;
tr[x].val = tr[ls(x)].val + tr[rs(x)].val + res;
}
void update(int x,int l,int r,int pos,int a,int b) {
if (l == r) {
tr[x].a += a,tr[x].b += b;
return;
}
int mid = l + r >> 1;
if (pos <= mid) update(ls(x),l,mid,pos,a,b);
else update(rs(x),mid + 1,r,pos,a,b);
pushup(x);
}
int n,a[N],b[N];
multiset<int> st;
signed main(){
cin >> n;
for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
for (int i = 1;i <= n;i ++) scanf("%lld",&b[i]);
int m = 1e5;
for (int i = 1;i <= n;i ++) {
st.insert(b[i]);
update(1,1,m,a[i],1,0);
update(1,1,m,b[i],0,1);
}
int mx = tr[1].val;
for (int i = 1;i <= n;i ++) {
update(1,1,m,a[i],-1,0);
int l = a[i] + 1,r = *st.rbegin(),res = -1;
while(l <= r) {
int mid = l + r >> 1;
auto t = st.lower_bound(mid);
if (t == st.end()) {
r = mid - 1;
continue;
}
update(1,1,m,*t,0,-1);
if (tr[1].val + (a[i] < *t) == mx) l = mid + 1,res = *t;
else r = mid - 1;
update(1,1,m,*t,0,1);
}
if (res != -1) {
st.erase(st.find(res));
update(1,1,m,res,0,-1);
printf("%lld ",res);
mx --;
continue;
}
l = *st.begin(),r = a[i],res = l;
while(l <= r) {
int mid = l + r >> 1;
auto t = st.lower_bound(mid);
if (t == st.end()) {
r = mid - 1;
continue;
}
update(1,1,m,*t,0,-1);
if (tr[1].val == mx) l = mid + 1,res = *t;
else r = mid - 1;
update(1,1,m,*t,0,1);
}
update(1,1,m,res,0,-1);
printf("%lld ",res);
st.erase(st.find(res));
}
return 0;
}

浙公网安备 33010602011771号