luogu P2862 [USACO06JAN]Corral the Cows G
题面传送门
这东西有一个更优的复杂度。
显然可以二分,二分以后对每一维尺取后算是\(O(nwlogw)\)的,可以过去。
但是我们发现在第二维尺取时的这一维\(w\)很没有必要,换句话说,很没效率。
所以就可以用一棵权值线段树代替掉,对每一个点向后\(mid\)个贡献权值。查询时查询最大值即可。
代码实现:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,l,r,mid,f[80039],sum[80039],ans;
struct yyy{int x,y;}s[10039];
inline bool cmp(yyy x,yyy y){return x.x<y.x;}
inline void push(int now){
f[now<<1]+=f[now];f[now<<1|1]+=f[now];
sum[now<<1]+=f[now];sum[now<<1|1]+=f[now];
f[now]=0;
}
inline void get(int x,int y,int z,int l,int r,int now){
if(x<=l&&r<=y){f[now]+=z;sum[now]+=z;return;}
if(f[now]) push(now);
int m=(l+r)>>1;
if(x<=m) get(x,y,z,l,m,now<<1);
if(y>m) get(x,y,z,m+1,r,now<<1|1);
sum[now]=max(sum[now<<1],sum[now<<1|1]);
}
inline int check(int mid){
memset(f,0,sizeof(f));memset(sum,0,sizeof(sum));
register int i,l=1,j;
for(i=1;i<=n;i++){
get(s[i].y,min(s[i].y+mid-1,ans),1,1,ans,1);
if(s[i+1].x!=s[i].x){
while(s[i].x-s[l].x+1>mid)get(s[l].y,min(s[l].y+mid-1,ans),-1,1,ans,1),l++;
if(sum[1]>=m) return 1;
}
}
return 0;
}
int main(){
register int i;
scanf("%d%d",&m,&n);
for(i=1;i<=n;i++) scanf("%d%d",&s[i].x,&s[i].y),ans=max(ans,max(s[i].y,s[i].x));
sort(s+1,s+n+1,cmp);
l=0;r=ans;
while(l+1<r){
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid;
}
printf("%d\n",r);
}

浙公网安备 33010602011771号