IC_QQQ

题解 P1502 【窗口的星星】

2019-05-04 16:33:14


前置芝士:扫~描~线~

这个题其实还有个清流的背景

尝试解决问题:

我们已知了窗子的长W和宽H,只需要确定矩形的右上角M,整个窗子的位置就确定了。

对于任意的一颗星星 (x,y),我们考虑M放在哪些区域可以得到她。

因为星星的坐标是整数,我们也令M是整数。

注意题目要求:边界不能放。所以,范围就出来了:

左下角$(x,y)$,右上角$(x+W-1,Y+H-1)$。(包括边界)

此时,问题转化成了:平面上有若干个区域,每个区域都带有一个权值,求在哪个坐标上重叠的区域权值和最大。

成功转化成了熟悉的扫~描~线问题。

套路:

取出每个星星形成的矩形,记做四元组

左边:$(x,y,y+H,k)$。

右边:$(x+W,y,y+H,-k)$。

k是星星的亮度。

然后以x为关键元,对四元组进行排序。

新的问题:x相等怎么办?

答案:右边界优先。

为什么呢?如果这里同时有一个左边界和一个右边界,右边界已经失效了,要先从扫描线中删除。(前面提到,有效范围是x~x+W-1)。

解决了问题,继续进行套路:

对纵坐标进行离散化,得到tot个代表数,这条扫描线就被分成了tot-1段。

c[i] 来维护这条扫描线,我们要得到扫描线上的最大值。因此,c[i] 表示扫描线上第 $i$ 段的权值。

走流程:

  • 遇到一条边,用这条边修改扫描线。
  • 扫一遍c数组,找到最大值,更新ans

怎么修改扫描线?我们令纵坐标y的代表数为val(y)

修改扫描线:c[val(y)~val(y+H)-1]都加上k

因为边界不能放,所以是val(y+H)-1

线段树维护c数组

每个节点主要是维护最大值dat。注意一下下传延迟标记就行了。

代码:

#include<bits/stdc++.h>
#define R register int
#define ll long long
using namespace std;
const int N=2*1e4+5;
int n,nn,t;
ll H,W,ans;
struct node{
    ll x,u,v,w;//u,v纵坐标
}da[N];//四元组 
struct aaa{
    ll dat,add;//最大值,懒标记 
}tree[4*N];
ll row[N],tot;//离散化 

ll in(){
    ll x=0;char ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}

bool cmp(node a,node b){//四元组排序
    if(a.x==b.x) return a.w<b.w;//重点,右边界优先 
    return a.x<b.x;
}

void reset(){//预处理,清零
    ans=0;tot=0;
    memset(da,0,sizeof(da));
    memset(row,0,sizeof(row));
    memset(tree,0,sizeof(tree));
    return;
}

void scan(){//读入
    n=in();W=in();H=in();
    int a,b,c;nn=n+n;
    for(R i=1;i<=n;i++){
        a=in();b=in();c=in();
        row[i]=b;row[i+n]=b+H;
        da[i].x=a;da[i+n].x=a+W;
        da[i].u=b;da[i+n].u=b;
        da[i].v=b+H;da[i+n].v=b+H;
        da[i].w=c;da[i+n].w=-c;
    }
    return;
}

void row_(){//离散化
    sort(row+1,row+1+nn);
    tot=unique(row+1,row+1+nn)-(row+1);
    return;
}

int query(ll x){//离散化
    return lower_bound(row+1,row+1+tot,x)-row;
}

void spread(int pos){//下传懒标记
    int ls=pos*2,rs=pos*2+1;
    tree[ls].dat+=tree[pos].add;
    tree[rs].dat+=tree[pos].add;
    tree[ls].add+=tree[pos].add;
    tree[rs].add+=tree[pos].add;
    tree[pos].add=0;
    return;
}

void change(int pos,int pl,int pr,int l,int r,ll w){
    if(l<=pl&&r>=pr){
        tree[pos].dat+=w;
        tree[pos].add+=w;
        return;
    }
    if(tree[pos].add) spread(pos);
    int mid=(pl+pr)>>1;
    if(l<=mid) change(pos*2,pl,mid,l,r,w);
    if(r>mid) change(pos*2+1,mid+1,pr,l,r,w);
    tree[pos].dat=max(tree[pos*2].dat,tree[pos*2+1].dat);
    return;
}

int main(){
    t=in();
    while(t--){     
        reset();scan();row_();              
        sort(da+1,da+1+nn,cmp);
        for(int i=1;i<=nn;i++){
            int u=query(da[i].u);
            int v=query(da[i].v)-1;
            change(1,1,tot-1,u,v,da[i].w);
            //懒得建树,将节点的左右端点作为参数传递 
            ans=max(ans,tree[1].dat);
        }
        printf("%lld\n",ans);
    }
    return 0;
}