BZOJ4411 - [Usaco2016 Feb]Load balancing

Portal

Description

给出平面上的\(n(n\leq10^5)\)个整点。画两条直线\(x=x_0\)\(y=y_0\)将这些点划分成\(s_1,s_2,s_3,s_4\)个点,最小化\(max\{s_1,s_2,s_3,s_4\}\)

Solution

二分答案+线段树。
首先进行离散化,记录\(sumY[i]\)表示\(y\leq i\)的点的个数。
检查\(m\)是否合法时,枚举\(x_0\)并查找是否存在合法的\(y_0\)。将↖称为\(s_1\),↙称为\(s_2\),↗称为\(s_3\),↘称为\(s_4\)。那么我们找到使得\(s_1,s_2\leq m\)的最大的\(y_0\),然后检查\(s_3,s_4\)是否不超过\(m\)。我们将\(x\leq x_0\)的点加入线段树,在线段树上二分来找到最大的\(y_0\)使得线段树上\([1,y_0]\)中的点数和(即\(s_1\))不超过\(m\)\(s_2=sumY[y_0]-s_1\)也不超过\(m\)。找到\(y_0\)后检查\(s_3=sum[rt]-s_1,s_4=n-s_1-s_2-s_3\)是否均不超过\(m\),若均合法则说明\(ans\leq m\),否则继续枚举\(x_0\)。若对于所有\(x_0\)均不存在合法的\(y_0\),则说明\(ans>m\)

时间复杂度\(O(nlog^2n)\)

Code

//[Usaco2016 Feb]Load balancing
#include <algorithm>
#include <cstdio>
using namespace std;
inline char gc()
{
    static char now[1<<16],*s,*t;
    if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
    return *s++;
}
inline int read()
{
    int x=0; char ch=gc();
    while(ch<'0'||'9'<ch) ch=gc();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x;
}
const int N=1e5+10;
int n,nX,nY; int sumY[N];
struct point{int x,y;} pt[N];
bool cmpX(point x,point y) {return x.x<y.x;}
int mapX[N],mapY[N];
void discrete()
{
    for(int i=1;i<=n;i++) mapX[i]=pt[i].x,mapY[i]=pt[i].y;
    sort(mapX+1,mapX+n+1); nX=unique(mapX+1,mapX+n+1)-mapX-1;
    for(int i=1;i<=n;i++) pt[i].x=lower_bound(mapX+1,mapX+nX+1,pt[i].x)-mapX;
    sort(mapY+1,mapY+n+1); nY=unique(mapY+1,mapY+n+1)-mapY-1;
    for(int i=1;i<=n;i++) pt[i].y=lower_bound(mapY+1,mapY+nY+1,pt[i].y)-mapY;
    sort(pt+1,pt+n+1,cmpX);
}
const int Ns=4e5+10;
#define Ls (p<<1)
#define Rs (p<<1|1)
int rt,sum[Ns]; int clr[Ns];
int optL,optR;
void update(int p) {sum[p]=sum[Ls]+sum[Rs];}
void pushdw(int p) {if(clr[p]) sum[Ls]=sum[Rs]=0,clr[Ls]=clr[Rs]=true,clr[p]=false;}
void ins(int p,int L0,int R0,int x,int v)
{
    if(L0==R0) {sum[p]+=v; return;}
    pushdw(p);
    int mid=L0+R0>>1;
    if(x<=mid) ins(Ls,L0,mid,x,v);
    else ins(Rs,mid+1,R0,x,v);
    update(p);
}
int query1(int p,int L0,int R0,int s1,int x)
{
    if(L0==R0) return L0;
    pushdw(p);
    int mid=L0+R0>>1; int t1=s1+sum[Ls],t2=sumY[mid]-t1;
    if(x<t1||x<t2) return query1(Ls,L0,mid,s1,x);
    else return query1(Rs,mid+1,R0,t1,x);
}
int query2(int p,int L0,int R0)
{
    if(optL<=L0&&R0<=optR) return sum[p];
    pushdw(p);
    int mid=L0+R0>>1; int res=0;
    if(optL<=mid) res+=query2(Ls,L0,mid);
    if(mid<optR) res+=query2(Rs,mid+1,R0);
    return res;
}
bool check(int m)
{
    rt=1; sum[rt]=0,clr[rt]=true;
    for(int i=1,t=1;i<=nX;i++)
    {
        while(pt[t].x==i) ins(rt,1,nY,pt[t].y,1),t++;
        if(sum[rt]>m+m) break;
        else if(n-sum[rt]>m+m) continue;
        int j=query1(rt,1,nY,0,m)-1;
        optL=1,optR=j; int s1=query2(rt,1,nY);
        int s2=sumY[j]-s1,s3=sum[rt]-s1,s4=n-s1-s2-s3;
        if(s1>m||s2>m) printf("query1 GG\n");
        if(s3<=m&&s4<=m) return true; 
    }
    return false;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) pt[i].x=read(),pt[i].y=read();
    discrete();
    for(int i=1;i<=n;i++) sumY[pt[i].y]++;
    for(int i=1;i<=nY;i++) sumY[i]+=sumY[i-1];
    int L=n/4-1,R=n;
    while(L<=R)
    {
        int mid=L+R>>1;
        if(check(mid)) R=mid-1;
        else L=mid+1;
    }
    printf("%d\n",L);
    return 0;
}

P.S.

sro Icefox的\(O(nlogn)\)做法 orz,比我强多啦

posted @ 2018-04-13 15:28  VisJiao  阅读(201)  评论(0编辑  收藏  举报