解法本身题解区已经写的较为清楚了,这里主要对大多数题解都涉及到但均未证明的一个贪心策略进行感性证明,故前面的推导过程写的较为简略。
给定
首先不难想到对于区间
然后 DP 显然,也很好想到一个状态
考虑优化,状态没什么可优化的,于是考虑优化转移,我们尝试提出来与转移时的
所以我们直接对前者维护一个最大值(因为我们的区间之间使有序的,所以如果对于之前的区间已经无交了,对于现在的一定也无交),后者则维护一个单调队列单调栈单调双端队列,每次处理时先把对应单调双端队列队头中所有不合法的,也就是不交的弹出然后更新不交区间里面的最大值。然后用交的和不交的里的最大值更新当前
如果你仔细想一下就会发现这个算法有一点问题,首先我们在最初的找单调双端队列里面的队头不合法的值的时候,我们时按照
这几个问题困扰了我很久,完全没有头绪,直到发现了一个性质我才大概能感性理解这个贪心的正确性。
显然当我们转移的时候,如果转移的这个
此时我们再回到刚才的考虑,我们的单调优先队列维护的是最大值,而最大值一定是
于是这个贪心(应该)是正确的。
当然上面这一大段证明都完全是我口糊的,不保证正确性,也不够理性和严谨,期待一个更严谨的证明。
xxxxxxxxxx
861
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26struct Line{
27 int l, r;
28 friend const bool operator < (const Line &a, const Line &b){if(a.l == b.l)return a.r < b.r; return a.l < b.l;}
29}line[110000];
30list < Line > _line;
31int N, K;
32int cnt(0);
33struct Status{int idx; int val;};
34deque < Status > bst[110000];
35int mx[110000];
36int dp[110000][110];
37
38int main(){
39 N = read(), K = read();
40 for(int i = 1; i <= N; ++i){
41 int l = read(), r = read();
42 _line.emplace_back(Line{l, r});
43 }_line.sort();
44 for(auto it = next(_line.begin()); it != _line.end();)
45 if(it->r <= prev(it)->r)it = _line.erase(it), --K; else ++it;
46 for(auto ln : _line)line[++cnt] = ln;
47 N = cnt; K = max(0, K);
48 for(int i = 1; i <= N; ++i){
49 for(int k = 0; k <= min(i - 1, K); ++k){
50 int xi = i - k - 1;
51 while(!bst[xi].empty() && line[bst[xi].front().idx].r < line[i].l){
52 auto tp = bst[xi].front(); bst[xi].pop_front();
53 mx[xi] = max(mx[xi], tp.val + line[tp.idx].r);
54 }
55 dp[i][k] = max({
56 dp[i][k],
57 mx[xi] + line[i].r - line[i].l,
58 bst[xi].empty() ? -1 : bst[xi].front().val + line[i].r
59 });
60 int val = dp[i][k] - line[i].r;
61 int pos = i - k;
62 while(!bst[pos].empty() && bst[pos].back().val < val)bst[pos].pop_back();
63 bst[pos].push_back(Status{i, val});
64 }
65 }int ans(-1);
66 for(int i = N - K; i <= N; ++i)ans = max(ans, dp[i][K - (N - i)]);
67 printf("%d\n", ans);
68 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
69 return 0;
70}
71
72template < typename T >
73inline T read(void){
74 T ret(0);
75 short flag(1);
76 char c = getchar();
77 while(c != '-' && !isdigit(c))c = getchar();
78 if(c == '-')flag = -1, c = getchar();
79 while(isdigit(c)){
80 ret *= 10;
81 ret += int(c - '0');
82 c = getchar();
83 }
84 ret *= flag;
85 return ret;
86}
update-2022_11_03 初稿