题意:
给出n个天然金矿石的位置,选一小块长方形的矿地,此矿地长和宽为s和w且平行于坐标系统的轴线。这块地的价值是这块区域内天然金矿石的数量。计算出这块地的最大可能价值。
( 1≤s,w≤10000,1≤n≤15000 1 ≤ s , w ≤ 10000 , 1 ≤ n ≤ 15000 )
矿石坐标 −30000≤x,y≤30000 − 30 000 ≤ x , y ≤ 30 000 )
Solution:
这道题有一个显然的暴力做法:枚举横行,在确定的带状区间中暴力
但是这样的复杂度显然过高
考虑如何优化:对于确定的带状区间,我们把一个纵坐标为y的点改成两个操作: f[y]+1,f[y+h+1]−1 f [ y ] + 1 , f [ y + h + 1 ] − 1 ,最后的答案即为f这个数组的最大前缀和,具体理由大家可以自己思考一下
那么问题就转为如何维护最大前缀和
线段树!
对于每个点所代表的区间,维护这个区间的sum,这个区间的最大前缀和,每次修改时维护一下即可(参考我的update操作)
每个点最多被加入删除一次,再加上离散化的复杂度,总复杂度为 O(nlog2n) O ( n l o g 2 n )
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int N=15010;
int n,w,h;
struct tree{
int l,r,v,maxv;
}tr[8*N];
struct Q{
int x,y;
}q[N];
int lsh[2*N],cnt,maxx,maxy,minx=1e5;
int ans=0;
vector<int> f[4*N];
void update(int i)
{
tr[i].v=tr[i<<1].v+tr[i<<1|1].v;
tr[i].maxv=max(tr[i<<1].maxv,tr[i<<1].v+tr[i<<1|1].maxv);
}
void build(int i,int l,int r)
{
tr[i].l=l;tr[i].r=r;
if (l==r) return;
int mid=l+r>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
}
void modify(int i,int pos,int x)
{
int l=tr[i].l,r=tr[i].r;
if (l==r) {
tr[i].v+=x;tr[i].maxv=max(0,tr[i].v);return;}
int mid=l+r>>1;
if (pos<=mid)modify(i<<1,pos,x);
else modify(i<<1|1,pos,x);
update(i);
}
int main()
{
scanf("%d%d",&w,&h);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d",&q[i].x,&q[i].y),lsh[++cnt]=q[i].x,lsh[++cnt]=q[i].y;
sort(lsh+1,lsh+1+cnt);
cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;
for (int i=1;i<=n;i++)
{
int y=lower_bound(lsh+1,lsh+1+cnt,q[i].y)-lsh;
f[q[i].x+30000].push_back(q[i].y);
maxx=max(maxx,q[i].x+30000);
minx=min(minx,q[i].x+30000);
maxy=max(y,maxy);
}
build(1,1,maxy);
for (int i=minx;i<=minx+w;i++)
{
for (int j=0;j<f[i].size();j++)
{
int p=lower_bound(lsh+1,lsh+1+cnt,f[i][j])-lsh;
modify(1,p,1);
p=upper_bound(lsh+1,lsh+1+cnt,f[i][j]+h)-lsh;
if (p<=maxy)modify(1,p,-1);
}
}
ans=max(ans,tr[1].maxv);
for (int i=minx+w+1;i<=maxx;i++)
{
for (int j=0;j<f[i-w-1].size();j++)
{
int p=lower_bound(lsh+1,lsh+1+cnt,f[i-w-1][j])-lsh;
modify(1,p,-1);
p=upper_bound(lsh+1,lsh+1+cnt,f[i-w-1][j]+h)-lsh;
if (p<=maxy)modify(1,p,1);
}
for (int j=0;j<f[i].size();j++)
{
int p=lower_bound(lsh+1,lsh+1+cnt,f[i][j])-lsh;
modify(1,p,1);
p=upper_bound(lsh+1,lsh+1+cnt,f[i][j]+h)-lsh;
if (p<=maxy)modify(1,p,-1);
}
ans=max(ans,tr[1].maxv);
}
printf("%d",ans);
}