P2862 [USACO06JAN]Corral the Cows G
考虑二分答案,问题转化为能否用边长为\(len\)的正方形框住大于等于\(c\)个草场。
继续转化问题,即边长为\(len\)的正方形能框住的最多草场数是否大于\(c\)。
考虑每一个右下角\((x,y)\),则每一个横坐标在\([x-len+1,x]\),纵坐标在\([y,y+len-1]\)中的草场都可以被框住。
转而考虑每一个草场\((x,y)\)的贡献,它对横坐标在\([x,x+len-1]\),纵坐标在\([y-len+1,y]\)的右下角都有贡献。
考虑扫描线,对于每一个草场可以看成两个线段,即\(\{x,y-len+1,y,1\}\)和\(\{x+len,y-len+1,y,-1\}\)。
即这个草场从\(x\)起在区间\([y-len+1,y]\)上生效,从\(x+len\)起失效。
线段树维护最大值即可,时间复杂度\(\Theta(nlogn)\)(二分+扫描线)。
代码如下,仅供参考:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int maxn=1e4+10;
const int N=1e4+5;
int n,m,ans,a[maxn],b[maxn],tot;
struct node{int pos,l,r,val;}p[maxn];
inline int cmp(node x,node y){
if(x.pos==y.pos)return x.val<y.val;
return x.pos<y.pos;
}
int tr[maxn<<4],lazy[maxn<<4];
inline void build(int h,int l,int r){
tr[h]=lazy[h]=0;
if(l==r)return;
int mid=(l+r)>>1;
build(h<<1,l,mid);
build(h<<1|1,mid+1,r);
}
inline void pushup(int h,int z){
tr[h]+=z;lazy[h]+=z;
}
inline void pushdown(int h){
if(!lazy[h])return;
pushup(h<<1,lazy[h]);
pushup(h<<1|1,lazy[h]);
lazy[h]=0;
}
inline void update(int h,int l,int r,int x,int y,int z){
if(l>y||r<x)return;
if(l>=x&&r<=y){pushup(h,z);return;}
pushdown(h);
int mid=(l+r)>>1;
update(h<<1,l,mid,x,y,z);
update(h<<1|1,mid+1,r,x,y,z);
tr[h]=max(tr[h<<1],tr[h<<1|1]);
}
inline int check(int len){
build(1,-N,N);tot=0;
for(int i=1;i<=n;i++){
p[++tot]=(node){a[i],b[i]-len+1,b[i],1};
p[++tot]=(node){a[i]+len,b[i]-len+1,b[i],-1};
}
sort(p+1,p+1+tot,cmp);
for(int i=1;i<=tot;i++){
update(1,-N,N,p[i].l,p[i].r,p[i].val);
if(tr[1]>=m)return 1;
}
return 0;
}
int main(){
m=read(),n=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=read();
int l=1,r=N;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}
深深地感到自己的弱小。