题目链接
https://cn.vjudge.net/problem/HDU-5124
胡扯
感觉说新方法好像有点不太好,但是翻了十几篇博客都是清一色离散化之类的…
为什么会做这道题呢?因为前几天做了套NOIp模拟赛,T1是坐标轴上的最大团,愉快地转化成最多不相交区间问题贪心处理
但是我把大于等于号看反了,以为区间相交才连边,于是转化成了”让你找出被覆盖次数最多的点(线段)被覆盖的次数”,也就是这题
然后考场上yy出了一个堆+贪心的鬼畜做法,对拍过了,以为能AC,结果……爆0了,很angry,于是找到了这道题,用yy出来的新算法交了一发居然就A了,跑得还不错?要是手写堆的话可能更快
分析
我先以左端点为第一关键字,右端点为第二关键字排序,然后遍历所有区间$seg$
遍历中$pos$记录当前满足$seg[i].l<=pos$所有区间的最大右端点值,如果新加入的区间左端点大于pos,pos置为$seg[i].r$,$cnt$重置为1
一个小根堆维护刚刚那些$seg[i].l<=pos$所有区间的右端点值,如果当前新加入的区间左端点大于堆顶值,弹出堆顶,同时$cnt-1$,反复操作直到不满足大于或是堆为空,接着更新答案.
排序$NlogN$,每个区间最多被弹出一次因此遍历一遍$NlogN$,因此总时间复杂度$O(N logN)$,本来想用Two Pointer但是好像不行
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cctype> #include <vector> #include <queue> #define ll long long #define ri register int using std::min; using std::max; using std::vector; using std::priority_queue; using std::sort; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c=='-'; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x;return ; } const int maxn=100005; const ll inf=1e17; struct Seg{ ll l,r; bool operator <(const Seg &b)const{ return l==b.l?r<b.r:l<b.l; } }seg[maxn]; int n; priority_queue <ll, vector<ll> ,std::greater<ll> >q; int main(){ ll x,len,l,r; int T; read(T); while(T--){ while(q.size())q.pop(); read(n); for(ri i=1;i<=n;i++){ read(l),read(r); seg[i].l=l,seg[i].r=r; } sort(seg+1,seg+1+n); ll pos=-inf; int ans=0,cnt=0,lst=0; for(ri i=1;i<=n;i++){ if(seg[i].l>pos){ while(q.size())q.pop(); pos=seg[i].r; ans=max(ans,cnt); cnt=1; } else cnt++; q.push(seg[i].r); if(seg[i].r>pos)pos=seg[i].r; if(q.size())lst=q.top(); while(seg[i].l>lst&&q.size()){ q.pop();cnt--; if(!q.empty())lst=q.top(); } ans=max(ans,cnt); } printf("%d\n",ans); } return 0; }
|
考试看错你会做的题的感觉真….难受