嘘~ 正在从服务器偷取页面 . . .

CF1889C2 Doremy's Drying Plan (Hard Version) 题解


CF1889C2 Doremy's Drying Plan (Hard Version) 题解

题目链接:Doremy's Drying Plan (Hard Version)

题意

多组数据。有 \(n\) 个点和 \(m\) 条线段,你可以选择 \(k\) 条线段删除,

要求最大化未被线段覆盖的点的数量,输出最大值。

数据范围

\(1 \le T \le 10^4,~1 \le \sum n,\sum m \le 2\times 10^5,~2 \le k \le \min(10, m),~1\le l_i \le r_i \le n\)

\(f(i,x,y)\) 为前 \(i\) 条线段,删除了 \(x\) 条,没有删除的线段的右端点是 \(y\) 时,覆盖位置的最小值。

把线段按左端点升序排序,这样转移考虑枚举当前线段是否被删除即可。

虽然 \(y\) 的值域非常大,但因为只有 \(k+1\) 个极大的右端点是有用的。

于是总状态数为 \(\mathcal{O}(mk^2)\) ,实现的时候可以用 \(i\times 2^{22} + x\times 2^{18} + y\) 的方式压到 unordered_map 里。

注意 operator[] 在访问不存在元素时,会自动创建一个元素,因此不要用 !mp[x] 的方式判断元素是否存在。

时间复杂度 \(\mathcal{O}(mk^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e5 + 15))
#define Fi first
#define Se second

pii a[N]; const int B = 1e9;
unordered_map<int,int> dp; set<int> st;
#define id(x, y, z) (((x) << 22) | ((y) << 18) | (z))
void upd(int x, int y, int z, int t) { down(dp[id(x, y, z)], t - B); }
void solve()
{
    int n, m, k; cin >> n >> m >> k;
    rep(i, 0, m - 1) cin >> a[i].Fi >> a[i].Se;
    if(k >= m) { cout << n << '\n'; return; }
    sort(a, a + m); dp.clear(); st.clear(); st.insert(0); upd(0, 0, 0, 0);
    rep(i, 0, m - 1)
    {
        for(int l : st) rep(j, 0, min(k, i))
        {
            if(!dp.count(id(i, j, l))) continue;
            const int t = dp[id(i, j, l)] + B; dp.erase(id(i, j, l));
            upd(i + 1, j, max(l, a[i].Se), t + max(a[i].Se, l) - max(a[i].Fi - 1, l));
            if(j < k) upd(i + 1, j + 1, l, t);
        }
        st.insert(a[i].Se); while(st.size() > k + 1) st.erase(st.begin());
    }
    int res = n + 1; 
    for(int l : st) rep(j, 0, k) down(res, dp[id(m, j, l)] + B);
    cout << n - res << '\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int qwq; cin >> qwq; while(qwq--) solve();
    return 0;
}

双倍经验:CF1889C1 Doremy's Drying Plan (Easy Version)绿的也算双倍?


参考文献

[1] https://www.luogu.com.cn/article/ayjkcwrd


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录