洛谷P3602 Koishi Loves Segments 贪心

时间:2023-08-25 20:46:56

正解:贪心

解题报告:

传送门!

首先在学习贪心的入门题的时候我们就知道,当x=1的时候,也就是每条线段不能相交的时候的做法——就按右端点排序然后能选就选,也就是会议安排问题,原因显然?就你选右端点更靠左的线段显然不会更劣

然后现在考虑x!=1了怎么做

于是考虑类似的套路,依然是右端点排序能选就选,考虑什么时候不能选了?就加入现在新加入了一条线段,然后导致有个点被覆盖次数超过x了,那肯定就是要从覆盖了这个点的边中删去一条?然后就考虑删哪条?那就显然是把右端点最靠右的给删了,证明的话,因为我们是从左到右一个个扫描点的,所以显然每次删最靠右的收益最大

具体实现的话,两个点?

一个是因为要一个个点地扫所以要离散化,显然只有端点和p有意义,就成功把复杂度降下来了QwQ

一个是,我们不是要支持删边操作balabla的嘛,就感觉比较难实现?所以考虑开个c数组记录到达这个点之后线段数量的变化,再开个全局变量t记录整个儿经过了几条线段,然后差分一下,这样每次删边就只会修改c[右端点]和t

(其实也可以,直接开个set,就直接支持一下删除最大值最小值操作就欧克了QwQ其实方便很多QAQ

over

关于细节二我好像说得不太清,,,还是不能弃疗所以还是强行再解释下趴,,,

首先我们要开个c数组,这个c数组是针对每个点开的,主要是用来记录这个到达这个点之后还存活的线段的数量的变化情况

然后考虑什么时候线段数量会有变化?一个是一条边的右端点经过了这个点,于是这条线段就不会经过这个点了,所以就--了

另一个是一条边被删了,然后本来因为这条边c--了,所以就++补回来就欧克了

over,放下不知道为什么跑得飞慢的代码QAQ

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define gc getchar()
#define ri register int
#define rc register char
#define rb register bool
#define rp(i,x,y) for(ri i=x;i<=y;++i) const int N=8e5+;
int n,m,st[N],l[N],r[N],p[N],x[N],tot,lim[N],tmp,c[N],as;
vector<int>nod[N];
priority_queue<int>Q; il int read()
{
rc ch=gc;ri x=;rb y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il int fd(ri x){return lower_bound(st+,st+tot+,x)-st;} int main()
{
// freopen("3602.in","r",stdin);freopen("3602.out","w",stdout);
n=read();m=read();rp(i,,n)st[++tot]=l[i]=read(),st[++tot]=r[i]=read();rp(i,,m)st[++tot]=p[i]=read(),x[i]=read();
sort(st+,st++tot);tot=unique(st+,st+tot+)-st-;rp(i,,n)nod[fd(l[i])].push_back(fd(r[i]));
memset(lim,,sizeof(lim));rp(i,,m)p[i]=fd(p[i]),lim[p[i]]=min(lim[p[i]],x[i]);
rp(i,,tot)
{
tmp+=c[i];
for(auto j:nod[i])--c[j+],++tmp,Q.push(j),++as;
while(tmp>lim[i])++c[Q.top()+],--tmp,--as,Q.pop();
}
printf("%d\n",as);
return ;
}