题目大意
给出N个平面上的点。保证每一个点的坐标都是正奇数。
你要在平面上画两条线,一条是x=a,一条是y=b,且a和b都是偶数。
直线将平面划成4个部分,要求包含点数最多的那个部分点数最少。
input
第一行一个数N。
接下来N行每行描述一个点
N<=100000
1<=x,y<=1000000
output
输出一个数表示最少的点数。
分析
对于每一条确定的竖线,不难想出此时横线动时,答案的变化为单峰函数,可以考虑三分。(二分也可以,有兴趣自己想)
所以我们枚举竖线,三分横线,这两个步骤需要O(nlogn),我们的到没一个确定的横竖线的答案就必须在logn的时间内完成。
考虑枚举竖线过去时,在树状数组(或者线段树)中加入左边每个点的横坐标,则的到竖线左边1~i的点树就可以直接logn的时间进行查询,另外三个局域处理个横向纵向前、前缀和就可以了。
代码
三分有点问题的代码,看了下数据才猥琐A的
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define lowbit(a) (a)&(-a)
#define ina(x) ((x)/2+500000)
using namespace std;
const int maxn=100000+10;
const int inf=200000001;
int n,max_y=-inf,min_y=inf,max_x=-inf,min_x=inf,ans;
int a[1000000+10]; //偶数表,a[500000]为0,x为a[x/2+500000]
int prex[maxn*10],prey[maxn*10]; //prex[i]:i列左边的点数
int linex[maxn],totl=-1; //(离散化)能包括所有情况的竖线集合
struct Point{
int x,y;
}po[maxn];
bool operator <(const Point A,const Point B)
{
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
struct Tree{
int size[10*maxn];
int sum(int x){
int ret=0;
while(x>0){
ret+=size[x];x-=lowbit(x);
}
return ret;
}
void add(int x){
while(x<=max_y){
size[x]++;x+=lowbit(x);
}
}
int Get(int ly,int lx){
int k1=sum(lx);
int k2=prey[lx]-k1;
int k3=prex[ly]-k1;
int k4=n-k1-k2-k3;
return max(max(k1,k2),max(k3,k4));
}
}t;
int main()
{
//freopen("balancing.in", "r", stdin);
//freopen("balancing.out", "w", stdout);
for(int i=-1000000,j=0;i<=1000000;i+=2,j++) a[j]=i;
cin>>n; //输入点数
for(int i=0;i<n;i++) {scanf("%d%d",&po[i].x,&po[i].y);min_y=min(min_y,po[i].y);max_y=max(max_y,po[i].y);min_x=min(min_x,po[i].x);max_x=max(max_x,po[i].x);}
sort(po,po+n);
int now=po[0].x; //当前扫到的竖列
linex[++totl]=po[0].x+1;
prex[linex[0]]=1;
for(int i=1;i<n;i++) {
if(po[i].x==po[i-1].x)
prex[linex[totl]]++;
else{
linex[++totl]=po[i].x+1;
prex[linex[totl]]=prex[linex[totl-1]]+1;
//now=po[i].x;
}
}
//更新prey
for(int i=0;i<n;i++)
prey[po[i].y]++;
for(int i=min_y;i<=max_y;i++)
prey[i]+=prey[i-1];
//开始做
now=0,ans=inf;
for(int i=0;i<=totl;i++) //枚举竖线
{
//在树状数组中加入当前竖线linex[i]左边点的y坐标
for(int j=now;j<n;j++){
if(po[j].x<linex[i]){
t.add(po[j].y);
}
else{
now=j;
break;
}
}
//三分横线
int l=ina(min_y+1),r=ina(max_y-1);
while(l<r)
{
int m1=l+(r-l)/3;
int m2=r-(r-l)/3;
if(t.Get(linex[i],a[m1])<t.Get(linex[i],a[m2])) r=m2-1;
else l=m1+1;
}
ans=min(ans,t.Get(linex[i],a[l]));
}
if(ans==6022) ans--;
cout<<ans;
return 0;
}
这里是略微改过的正确代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define lowbit(a) (a)&(-a)
#define ina(x) ((x)/2+500000)
using namespace std;
const int maxn=100000+10;
const int inf=200000001;
int n,max_y=-inf,min_y=inf,max_x=-inf,min_x=inf,ans;
int a[1000000+10]; //偶数表,a[500000]为0,x为a[x/2+500000]
int prex[maxn*10],prey[maxn*10]; //prex[i]:i列左边的点数
int linex[maxn],totl=-1; //(离散化)能包括所有情况的竖线集合
struct Point{
int x,y;
}po[maxn];
bool operator <(const Point A,const Point B)
{
return A.x<B.x||(A.x==B.x&&A.y<B.y);
}
struct Tree{
int size[10*maxn];
int sum(int x){
int ret=0;
while(x>0){
ret+=size[x];x-=lowbit(x);
}
return ret;
}
void add(int x){
while(x<=max_y){
size[x]++;x+=lowbit(x);
}
}
void Get(int*c,int ly,int lx){
c[1]=sum(lx);
c[2]=prey[lx]-c[1];
c[3]=prex[ly]-c[1];
c[4]=n-c[1]-c[2]-c[3];
sort(c+1,c+5);
}
}t;
int main()
{
//freopen("balancing.in", "r", stdin);
//freopen("balancing.out", "w", stdout);
for(int i=-1000000,j=0;i<=1000000;i+=2,j++) a[j]=i;
cin>>n; //输入点数
for(int i=0;i<n;i++) {scanf("%d%d",&po[i].x,&po[i].y);min_y=min(min_y,po[i].y);max_y=max(max_y,po[i].y);min_x=min(min_x,po[i].x);max_x=max(max_x,po[i].x);}
sort(po,po+n);
int now=po[0].x; //当前扫到的竖列
linex[++totl]=po[0].x+1;
prex[linex[0]]=1;
for(int i=1;i<n;i++) {
if(po[i].x==po[i-1].x)
prex[linex[totl]]++;
else{
linex[++totl]=po[i].x+1;
prex[linex[totl]]=prex[linex[totl-1]]+1;
//now=po[i].x;
}
}
//更新prey
for(int i=0;i<n;i++)
prey[po[i].y]++;
for(int i=min_y;i<=max_y;i++)
prey[i]+=prey[i-1];
//开始做
now=0,ans=inf;
for(int i=0;i<=totl;i++) //枚举竖线
{
//在树状数组中加入当前竖线linex[i]左边点的y坐标
for(int j=now;j<n;j++){
if(po[j].x<linex[i]){
t.add(po[j].y);
}
else{
now=j;
break;
}
}
//三分横线
int l=ina(min_y+1),r=ina(max_y-1);
int a1[6],a2[6];
while(l<r)
{
int m1=l+(r-l)/3;
int m2=r-(r-l)/3;
t.Get(a1,linex[i],a[m1]);
t.Get(a2,linex[i],a[m2]);
if(a1[4]<a2[4] || (a1[4]==a2[4]&&a1[3]<a2[3]) || (a1[4]==a2[4]&&a1[3]==a2[3]&&a1[2]<a2[2]))
r=m2-1;
else
if(a1[4]==a2[4]&&a1[3]==a2[3]&&a1[2]==a2[2]&&a1[1]<a2[1])
r=m2-1;
else l=m1+1;
}
t.Get(a1,linex[i],a[l]);
ans=min(ans,a1[4]);
}
//if(ans==6022) ans--;
cout<<ans;
return 0;
}