题解 P6172 【[USACO16FEB]Load Balancing P】

· · 题解

我看到这个题目是真的没思路,然后看题解还只有一个而且没几行讲思路,快哭了QAQ

为了避免别人也有我这种糟糕的体验,这篇题解诞生了。

先看题目,最大值最小,学过OI的都知道这要用到二分,直接二分枚举答案再check就好了。

再看check要怎么写。

设我们枚举的答案是ans,则我们分出来的每个区域的奶牛都要小于ans显而易见

我们枚举 y ,用两个树状数组维护 y 上面区域和下面的奶牛数,然后求一个最优的 x

但是如果直接枚举x,时间复杂度会直接到n^2,很明显不能直接枚举。

再仔细想想,假设我们是从小到大枚举y,那么上面区域的奶牛数会越来越少,下面的奶牛的数量会越来越多,所以对于上面区域,我们从小到大枚举x,设左上区域奶牛数刚好<=ansx=t1,对于下面区域,我们从大到小枚举x,设左下区域奶牛数刚好<=ansx=t2,则最优的x就是min(t1,t2)

```cpp #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1e5 + 10; inline int read() { int res=0; char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+(ch^48),ch=getchar(); return res; } struct Cow{ int x,y; bool operator <(const Cow a) const{ return y<a.y; } }c[N]; int n,sb[N],xb[N]; #define lowbit(x) (x)&(-x) void change(int a[],int x,int k) { while(x<=n) a[x]+=k,x+=lowbit(x); } int query(int a[],int x) { int res=0; while(x>0) res+=a[x],x-=lowbit(x); return res; } bool check(int x) { memset(sb,0,sizeof(sb)); memset(xb,0,sizeof(xb)); for(int i=1;i<=n;i++) change(sb,c[i].x,1); int st=n,xt=0,zs=1,zx=n; for(int t,i=1,j=1;i<=n;i=j) { while(c[i].y==c[j].y) change(sb,c[j].x,-1),change(xb,c[j].x,1),st--,xt++,j++; while(zs<=n&&query(sb,zs)<=x) zs++; zs--; while(zx>0&&query(xb,zx)>x) zx--; t=min(zx,zs); if(xt-query(xb,t)<=x&&st-query(sb,t)<=x) return true; } return false; } pair<int,int>p[N]; int tot,mid,l,r,ans; int main() { n=read(); for(int i=1;i<=n;i++) c[i].x=read(),c[i].y=read(),p[i].first=c[i].x,p[i].second=i; sort(p+1,p+n+1); for(int i=1;i<=n;i++) { if(p[i].first!=p[i-1].first) tot++; c[p[i].second].x=tot; } sort(c+1,c+n+1); l=1,r=n; while(l<=r) { mid=(l+r)>>1; if(check(mid)) r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); return 0; } ```