https://codeforces.com/contest/2047/problem/E
1.离散化
2.从左到右遍历,对每一个x,求出当分界点的x左边为当前位置时,最大每个部分的最小值。
3.关键在如何求这个max(min)。易知可以用一个树状数组、线段树之类的处理任意y范围内有多少个点。这里我们可以用两个树状数组,维护分界线左右两边,任意y范围内的点数。
4.可以用二分解决,但是不是很好看出来,因为没有直观的单调性。二分的思路是,观察分界线,发现一方较小时,分界线应该往另一方移动。所以最优点是
(1)最小值第一次不出现在上方的分界线。
(2)最小值最后一次出现在上方的线。
对每个点二分两次,找出最优分界线的y坐标。然后对每个位置取全局最优的分界点坐标即可。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
#define MULTI_TEST
struct Fenwick{
vector<ll> tr;
int n;
Fenwick(int n):n(n){tr.assign(n+1,0);}
void init(int _n){
n = _n;
tr.assign(n+1,0);
}
void modify(ll x,ll val){
for(;x<=n;x+=x&-x)
tr[x]+=val;
}
ll query(ll x){
ll ans = 0;
for(;x;x-=x&-x)
ans+=tr[x];
return ans;
}
ll rangeSum(ll l,ll r){
return query(r) - query(l-1);
}
};
const int N = 1e5+10;
int lsh[2*N],cnt=0,len;
int n;
ll get_val(ll x){
return lsh[x];
}
void solve(){
cnt = 0;
lsh[0] = -1e9;
cin>>n;
vector<pll> point(n+1);
for(int i=1;i<=n;i++){
cin>>point[i].first>>point[i].second;
lsh[++cnt] = point[i].first;
lsh[++cnt] = point[i].second;
}
sort(lsh+1,lsh+cnt+1);
len = unique(lsh+1,lsh+cnt+1) - lsh - 1;
for(int i=1;i<=n;i++){
auto &[l,r] = point[i];
l = lower_bound(lsh+1,lsh+len+1,l) - lsh;
r = lower_bound(lsh+1,lsh+len+1,r) - lsh;
}
lsh[len+1] = 1e9;
Fenwick trl(len+1),trr(len+1);
for(int i=1;i<=n;i++){
auto [x,y] = point[i];
trr.modify(y,1);
}
ll cntx = 0;
ll ans = -1,ansx=0,ansy = 0;
sort(point.begin()+1,point.end());
auto get_num = [&](ll x)->array<ll,4>{
ll x1 = trr.rangeSum(x,len);
ll x2 = trl.rangeSum(x,len);
ll x3 = n-cntx - x1;
ll x4 = cntx - x2;
return {x1,x2,x3,x4};
};
for(int cur=1,idx=1;cur<=len+1;cur++){
//大于等于cur的位置是属于右边的
while(idx<=n && point[idx].first < cur){//把所有横坐标小于x的点加到左边
trl.modify(point[idx].second,1);
trr.modify(point[idx].second,-1);
cntx++;
idx++;
}
//idx是第一个横坐标大于等于cur的点或n+1
int l = 1,r = len;
//二分最后一个最小值出现在上方的点
while(l<r){
int mid = (l+r)>>1;
auto [x1,x2,x3,x4] = get_num(mid);
int mn = min({x1,x2,x3,x4});
//if(mn>ans){ans = mn,ansx = cur,ansy = mid;}
if(mn ==x1 || mn ==x2){
r = mid;
}else l = mid+1;
}
auto [x1,x2,x3,x4] = get_num(l);
int mn = min({x1,x2,x3,x4});
if(mn>ans){ans = mn,ansx = cur,ansy = l;}
//二分第一个最小值不出现在上方的点。
l = 1,r = len;
while(l<r){
int mid = (l+r+1)>>1;
auto [x1,x2,x3,x4] = get_num(mid);
int mn = min({x1,x2,x3,x4});
//if(mn>ans){ans = mn,ansx = cur,ansy = mid;}
if(mn != x1 && mn != x2){
l = mid;
}else r = mid-1;
}
auto [y1,y2,y3,y4] = get_num(l);
mn = min({y1,y2,y3,y4});
if(mn>ans){ans = mn,ansx = cur,ansy = l;}
}
ansx = get_val(ansx);
ansy = get_val(ansy);
cout<<ans<<endl<<ansx<<" "<<ansy<<endl;
}
int main(){
ifstream test_file("in.txt");
if (test_file) {
freopen("in.txt", "r", stdin);
freopen("output.txt", "w", stdout);
}
std::ios::sync_with_stdio(0);std::cout.tie(0);std::cin.tie(0);
int T = 1;
#ifdef MULTI_TEST
cin>>T;
#endif
while(T--){
solve();
}
return 0;
}