补题记录:2021牛客多校第二场题解
2021牛客多校第二场G:League of Legends 题解
题意
给你\(n\)个区间,将这些区间分成\(k\)组,使得每组内的任意俩区间都有交集,求总的最大交集。
思路
首先我们考虑大区间包含小区间的情况,当一个大区间包含小区间的时候,对于大区间,我们可以将其单独放在一个区间。这样能保证收益最大。剩下的我们可以排序,根据左区间来进行\(dp\)
\(dp\)表示:\(dp[i][j]\)表示前\(i\)个区间分成\(j\)组的最大收益
\(dp\)转移:\(dp[i][j] = max(dp[k - 1][j - 1] + R[k] - L[i]) (R[k] > L[i], 1 \le k \le i)\)
直接做的话,时间复杂度是\(n^3\)的,但是我们注意到,可以将\(dp[k - 1][j] + R[k]\)这部分看作一个整体,维护一个单调队列即可
#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 5050;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}
int dp[N][N], q[N], hh, tt;
int pre[N], re[N];
inline void solve() {
int n, k; cin >> n >> k;
vector<PII> p(n + 1), a(n + 1);
int c1 = 0, c2 = 0;
for (int i = 1; i <= n; i ++ ) cin >> p[i].x >> p[i].y;
sort(p.begin() + 1, p.end());
//开始处理大区间
int mxr = INF;
for (int i = n; i; i -- ) {
if (p[i].y < mxr) {
mxr = p[i].y;
a[++ c1] = p[i];
} else re[++ c2] = p[i].y - p[i].x;
}
sort(a.begin() + 1, a.begin() + 1 + c1);
memset(dp, -0x3f, sizeof dp);
dp[0][0] = 0;
for (int j = 1; j <= k; j ++ ) {
hh = 1, tt = 0;
for (int i = 1; i <= c1; i ++ ) {
while (hh <= tt && a[q[hh]].y <= a[i].x) hh ++;//注意这里,我们一般的弹出队头都是if,这里是因为有多个队头未满足才弹出多个
while (hh <= tt && dp[q[tt] - 1][j - 1] + a[q[tt]].y <= dp[i - 1][j - 1] + a[i].y) tt --;
q[++ tt] = i;
dp[i][j] = dp[q[hh] - 1][j - 1] + a[q[hh]].y - a[i].x;
}
}
//最后单独处理大区间,分别单独成组,记录最大值即可
sort(re + 1, re + 1 + c2, greater<int>());
for (int i = 1; i <= c2; i ++ ) pre[i] = pre[i - 1] + re[i];
int res = 0;
for (int i = 0; i <= c2; i ++ ) {
if (k < i) break;
res = max(res, dp[c1][k - i] + pre[i]);
}
cout << res << endl;
}
int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
auto now = clock();
#endif
ios::sync_with_stdio(false), cin.tie(nullptr);
cout << fixed << setprecision(2);
// int T; cin >> T;
// while (T -- )
solve();
#ifdef DEBUG
cout << "============================" << endl;
cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
return 0;
}