HDU 6882 Divide and Conquer(二分)
题面
\(使用两条相交直线平分n个点,使得每块区域都包含\frac{n}{4}个点,保证n\%4\equiv0\)
Solution
首先我们可以明确此题一定是有解的,所以我们可以首先固定一条平分线再去寻找第二条平分线,那么第一条平分线我们可以通过将所有点按照\(x,y\)排序,选取中间\([\frac{n}{2}-1]和[\frac{n}{2}]\)两个点,选取一条斜率接近\(\frac{\pi}{2}\)的直线平分即可,那么第二条线的斜率就是在\([-\frac{\pi}{2},\frac{\pi}{2}]\)之间,我们需要找到这样一条直线满足和第一条直线恰好平分一侧的\(\frac{n}{2}\)个点,我们可以发现这条直线的斜率是可以进行二分的,我们可以将每个点按照此斜率投射到之前第一条直线上,排序后直接可以遍历记录左侧点和右侧点的个数,如果一侧点不够\(\frac{n}{2}另一侧点多于\frac{n}{2}\)个点,那么我们可以将斜率往点少的一侧倾斜即可,最后可以得到恰好平分的斜率以及那个平分位置前一个点,那么直接将一条直接经过该点并平行上一丢丢距离就可以完成构造,总体复杂度\({O(nlog^2n)}\)
// ——By DD_BOND
//#include<bits/stdc++.h>
//#include<unordered_map>
//#include<unordered_set>
#include<functional>
#include<algorithm>
#include<iostream>
//#include<ext/rope>
#include<iomanip>
#include<climits>
#include<cstring>
#include<cstdlib>
#include<cstddef>
#include<cstdio>
#include<memory>
#include<vector>
#include<cctype>
#include<string>
#include<cmath>
#include<queue>
#include<deque>
#include<ctime>
#include<stack>
#include<map>
#include<set>
#define fi first
#define se second
#define pb push_back
#define MP make_pair
//#pragma GCC optimize(3)
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using namespace std;
typedef double db;
typedef long long ll;
typedef pair<db,db> Pd;
typedef pair<int,int> P;
typedef pair<ll,ll> Pll;
const db eps=1e-8;
const int MAXN=1e3+10;
const db pi=acos(-1.0);
const ll INF=0x3f3f3f3f3f3f3f3f;
inline int dcmp(db x){
if(fabs(x)<eps) return 0;
return (x>0? 1: -1);
}
inline db Sqrt(db x){
return x>0? sqrt(x): 0;
}
inline db sqr(db x){ return x*x; }
struct Point{
db x,y;
Point(){ x=0,y=0; }
Point(db _x,db _y):x(_x),y(_y){}
void input(){
double _x,_y;
scanf("%lf%lf",&_x,&_y);
x=_x,y=_y;
}
bool operator <(const Point &b)const{
return (dcmp(x-b.x)==0? dcmp(y-b.y)<0 : x<b.x);
}
db operator ^(const Point &b)const{ //叉积
return x*b.y-y*b.x;
}
db operator *(const Point &b)const{ //点积
return x*b.x+y*b.y;
}
Point operator +(const Point &b)const{
return Point(x+b.x,y+b.y);
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
Point operator *(db a){
return Point(x*a,y*a);
}
Point operator /(db a){
return Point(x/a,y/a);
}
};
inline db cross(Point a,Point b){ //叉积
return a.x*b.y-a.y*b.x;
}
inline db dot(Point a,Point b){ //点积
return a.x*b.x+a.y*b.y;
}
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e):s(_s),e(_e){} //两点确定直线
db polar(){ //极角
return atan2(e.y-s.y,e.x-s.x); //返回与x轴正向夹角(-pi~pi]
}
};
Point line_intersection(Line a,Line v){ //直线交点 调用前确保有交点
db a1=cross(v.e-v.s,a.s-v.s);
db a2=cross(v.e-v.s,a.e-v.s);
return Point((a.s.x*a2-a.e.x*a1)/(a2-a1),(a.s.y*a2-a.e.y*a1)/(a2-a1));
}
Line l1,l2;
Point point[50010];
pair<db,int>st[50010];
int main(void){
int t; scanf("%d",&t);
while(t--){
int n; scanf("%d",&n);
for(int i=0;i<n;i++) point[i].input();
sort(point,point+n);
if(point[n/2-1].x==point[n/2].x){
int sum=point[n/2-1].y+point[n/2].y;
l1=Line(Point(point[n/2].x-1,-9e8+sum/2),Point(point[n/2].x+1,9e8+sum/2+(sum%2)));
}
else l1=Line(Point(point[n/2-1].x,-9e8),Point(point[n/2].x,9e8));
int las=0;
db l=l1.polar()-pi+eps,r=l1.polar()-eps,theta=-1;
for(int t=1;t<=60;t++){
int f=0;
db mid=(l+r)/2;
Point vec(1e6*cos(mid),1e6*sin(mid));
for(int i=0;i<n;i++) st[i]={line_intersection(l1,Line(point[i],point[i]+vec)).y,i};
sort(st,st+n);
for(int i=0,ls=0,rs=0;i<n;i++){
if(i){
if(dcmp(st[i-1].fi-st[i].fi)==0){
if(st[i].se<n/2) ls++;
else rs++;
}
else{
if(ls==n/4&&rs==n/4){ f=1,las=st[i-1].se,theta=mid; break; }
else if(ls>=n/4){ l=mid; break; }
else if(rs>=n/4){ r=mid; break; }
if(st[i].se<n/2) ls++;
else rs++;
}
}
else{
if(st[i].se<n/2) ls++;
else rs++;
}
}
if(f) break;
}
l2=Line(point[las]+Point(9e8*cos(theta),9e8*sin(theta)),point[las]-Point(9e8*cos(theta),9e8*sin(theta)));
printf("%d %d %d %d\n",(int)round(l1.s.x),(int)round(l1.s.y),(int)round(l1.e.x),(int)round(l1.e.y));
printf("%d %d %d %d\n",(int)round(l2.s.x),(int)round(l2.s.y+0.9999),(int)round(l2.e.x),(int)round(l2.e.y+0.999));
}
return 0;
}