题目大意ios
给定一个环,环上有一些线段,试选出最多的线段算法
题解:优化
提醒:这多是一篇很是欢乐的题解spa
咱们考虑倍长环,而后断环为链code
咱们考虑枚举开头的线段,而后作一次贪心get
这样子的复杂度根据实现的不一样是\(O(n^2 \log n)\)或者\(O(n^2)\)string
不妨假设咱们不知道倍增能优化,咱们考虑答案的构成,记答案为\(B\)it
若是\(B < \sqrt n\),那么咱们只须要每次跳\(B\)次就能够出解io
若是\(B > \sqrt n\),那么咱们随机取\(\frac{n}{B}\)个线段做为端点,而后取最优值class
这样子,咱们就获得了一个看起来彻底不对劲的\(O(n \sqrt n)\)的算法
可是人都是懒惰的,咱们考虑直接用第二种方法
通过实践,实际上只要取\(2\)个线段做为端点就足够\(A\)掉本题了
复杂度\(O(n \log n)\),虽然正确性彻底没法保证呢
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define ri register int #define rep(io, st, ed) for(ri io = st; io <= ed; io ++) #define drep(io, ed, st) for(ri io = ed; io >= st; io --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } const int sid = 2e5 + 5; int n, m, tot; int L[sid], R[sid]; struct seg { int l, r; seg() {} seg(int _l, int _r) : l(_l), r(_r) {} friend bool operator < (seg a, seg b) { return a.r > b.r; } } A[sid]; priority_queue <seg> q; inline int solve(int o) { int ret = 0, nr = -1; rep(i, 1, tot) if(A[i].l >= L[o] && A[i].r <= L[o] + m) q.push(A[i]); while(!q.empty()) { seg B = q.top(); q.pop(); if(B.l < nr) continue; nr = max(nr, B.r); ret ++; } return ret; } int main() { m = read(); n = read(); rep(i, 1, n) { int l = read(), r = read(); L[i] = l; R[i] = r; if(l > r) A[++ tot] = seg(l, r + m); else { A[++ tot] = seg(l, r); A[++ tot] = seg(l + m, r + m); } } int ans = 0, k = 2; for(ri i = 1; i <= n; i += n / k) ans = max(ans, solve(i)); printf("%d\n", ans); return 0; }