窗内的星星不太一样的解法
前言
虽然这个题老师布置在扫描线里面的,看上去也是转化成矩形去求解,但我的第一反应并不是扫描线,并想到了一个个人感觉比较妙的思路。
文章可能比较啰嗦,谅解。
思路
我们可以简化一下题意:用一个长为
我们可以简单举个例子:
假设我们现在要拿高度为2,宽度为4的矩形去框星星。
我们不难想到一种降维的方法,就是可以先对所有点的
我们知道,假设我们定下了矩阵上面宽的高度,那么下面宽肯定是尽可能的向下取,也就可以取到上面宽高度减去限定高度再+1。(显然这种贪心思想是正确的,因为你多取一排肯定比少取一排要好。)
而且,为了避免浪费,我们可以让矩阵高度定在恰好一个点上方一点的位置。(首先,如果你不定在一个点上方一点的位置,那么你显然可以往下再框一行避免浪费,而不是去占一个空行。所以这种贪心思想仍然正确。)
然后用一种双指针的方法,分别指向两个点,来控制两个指针之间的所有点他的高度差在高度限制 以内,符合能被高度极限但宽度无限的矩形包围。
比如说,我们现在
但是,当我们枚举到
那么接下来,就是考虑宽度的问题了。
由于我们已经保证当前选进去的这些点,他的高度无论你在确定上边宽,不确定长的条件下怎么框他都能被框进去,那这个问题其实就可以转化为1维的给定数轴上的一些点及其坐标,然后用一个长度为
首先,我们不难想到一种暴力的思路,就是将这些
但这样时间复杂度为
那为什么会死呢?
注意,关键点来了
我们发现,由于我们的双指针移动,会删除一些点,并加入一些点,这就会导致答案的改变。而答案改变我们就需要重新算答案,这势必会浪费很多时间。
接着我们又看,我们对于宽度肯定也是框的越多越好,那么我们可以改变一下做题策略。我们可以先将所有的
然后,我们就可以把每个点作为左端点和他能框到的最右端点当作一个区间,并且最优答案肯定出在这些区间。当我们加入和删除一个点的时候,我们就只需要把所有包含了这个点的区间的答案进行修改,而不是把所有的修改。
又由于我们刚刚证明了右端点满足单调性,所有包含一个点的区间,他们肯定构成的又是一个整的区间。(可能有点抽象,需要自己思考一下。)
所以,既然修改一个点,我们修改的是一整个区间,那我们就可以用线段树的区间修改单点查询维护了。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int x,y,c;
bool operator <(const node &n)const
{
return y<n.y||(y==n.y&&x<n.x);
}
}arr[400005];
int n;
int ls[400005],lslen;
int nxt[400005];
int down[400005],up[400005];
struct segmentree
{
int l,r;
long long maxx,tag;
}tree[2000005];
void build(int p,int l,int r)
{
tree[p].l=l,tree[p].r=r;
tree[p].maxx=tree[p].tag=0;
if(l==r) return;
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void update(int p)
{
tree[p].maxx=max(tree[p<<1].maxx,tree[p<<1|1].maxx);
}
void pushdown(int p)
{
if(tree[p].tag)
{
int tag=tree[p].tag,l=p<<1,r=p<<1|1;
tree[l].tag+=tag,tree[r].tag+=tag;
tree[l].maxx+=tag,tree[r].maxx+=tag;
tree[p].tag=0;
}
}
void change(int p,int l,int r,int x)
{
if(tree[p].l>r||tree[p].r<l) return;
if(tree[p].l>=l&&tree[p].r<=r)
{
tree[p].maxx+=1ll*x;
tree[p].tag+=x;
return;
}
pushdown(p);
change(p<<1,l,r,x);
change(p<<1|1,l,r,x);
update(p);
}
int w,l;
int main()
{
while(~scanf("%d%d%d",&n,&w,&l))
{
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&arr[i].x,&arr[i].y,&arr[i].c);
ls[i]=arr[i].x;
}
sort(arr+1,arr+n+1);
sort(ls+1,ls+n+1);
lslen=unique(ls+1,ls+n+1)-ls-1;
build(1,1,lslen);
for(int i=1;i<=n;++i) arr[i].x=lower_bound(ls+1,ls+lslen+1,arr[i].x)-ls;
for(int i=1;i<=lslen;++i)
{
nxt[i]=lower_bound(ls+1,ls+lslen+1,ls[i]+w)-ls-1;
}
int last=1;
for(int i=1;i<=n;++i)
{
int l=lower_bound(nxt+1,nxt+lslen+1,arr[i].x)-nxt;
down[i]=l;//up down分别处理包含一个点的区间所构成的区间左右端点。
up[i]=lslen;
//cout<<i<<" "<<down[i]<<" "<<up[i]<<endl;
}
long long ans=0;
for(int i=1;i<=n;++i)
{
while(arr[i].y-arr[last].y>=l&&last<=n)
{
change(1,down[last],up[last],-1*arr[last].c);
last++;
}
change(1,down[i],up[i],arr[i].c);
ans=max(ans,tree[1].maxx);
}
cout<<ans<<endl;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】