bzoj2951: [Poi2001]Goldmine-线段树

题意:
给出n个天然金矿石的位置,选一小块长方形的矿地,此矿地长和宽为s和w且平行于坐标系统的轴线。这块地的价值是这块区域内天然金矿石的数量。计算出这块地的最大可能价值。
1s,w10000,1n15000 1 ≤ s , w ≤ 10000 , 1 ≤ n ≤ 15000
矿石坐标 30000x,y30000 − 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
#include
#include
#include
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;jint 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;j1].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;jint 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);
}

你可能感兴趣的:(线段树)