【Usaco2016 FEB】Load Balancing

题目大意

给出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;
}

你可能感兴趣的:(Usaco2016)