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
",ans);
	return 0;
}

深深地感到自己的弱小。

原文地址:https://www.cnblogs.com/syzf2222/p/13982904.html