12

https://zhengruioi.com/problem/3430

我觉得代码会比文字清楚!

cin >> n >> m; ans = n;
for (int i = 1; i <= n; i++) {
    int l, r; cin >> l >> r;
    a[r + 1].push_back(l);
}
for (int i = 1; i <= sz; i++) lim[i] = n;
for (int i = 1; i <= m; i++) {
    int x, y; cin >> x >> y;
    lim[x] = min(lim[x], y);
}
for (int i = 2; i <= sz; i++) lim[i] = min(lim[i], lim[i - 1]);
for (int i = sz; i >= 1; i--) {
    for (auto k : a[i]) q.push(k);
    while (q.size() > lim[i]) {
        int x = q.top(); q.pop();
        a[x].push_back(0); ans--;
    }
}
cout << ans << '\n';

所以这种贪心应该怎么想啊???

https://atcoder.jp/contests/agc023/tasks/agc023_d

神秘题,但是感觉还挺有意思的。

考虑如果 \(p_1<p_n\),那么 \(1\) 回家的时间肯定在 \(n\) 之后,且 \(n\) 回家之后车会一路从 \(n\) 开到 \(1\)。证明是显然的。

所以 \(1\) 的目标变成了让 \(n\) 尽快回家。那么可以令 \(p_n\) 加上 \(p_1\),然后对 \([2,n]\) 这个区间递归求解。\(p_1\ge p_n\) 的情况是类似的。

https://zhengruioi.com/problem/3419

\(ans_i\) 表示 \(i\) 的答案,考虑给 \(ans\) 找一个转移顺序。

枚举从 \(i\) 开始的路径上第一个满足 \(b_j>b_i\)\(j\),然后把 \(ans_j\) 转移给 \(ans_i\)。这个转移要成立的条件是,存在一条从 \(i\)\(j\) 的路径,满足对于路径上的每个点 \(k\),都有 \(b_k\le b_i\le c_k\)。所以如果按照 \(b\) 从大到小转移,每个点 \(k\) 存活的时间都是一段区间,然后线段树分治+可撤销并查集就做完了。

启发:找转移顺序。

https://codeforces.com/contest/2165/problem/D

这是最小链覆盖。所以考虑把每个数拆成入点和出点,然后 \(a_i\) 的出点向满足 \(j>i\)\(a_j=a_i\pm1\) 的入点连边。这样 \(n-最大匹配\) 就是答案。

根据奇偶性,这个图会天然地被分成两个连通块。考虑包含 \(1\) 的出点和 \(2\) 的入点的那个连通块,在求这个连通块的最大匹配时,我们一定是先最大化 \(1\)\(2\) 之间的匹配数量,并在最大化这个匹配的同时,去最小化匹配里所有 \(2\) 点的下标。这个可以直接贪心,从小到大枚举 \(1\) 点,对于每个点,都选 \(2\) 点里大于它的最小的。然后去最大化 \(2\)\(3\) 之间的匹配数量,并同时去最大化匹配里所有 \(3\) 点的下标,以此类推。

正确性感觉很显然。时间复杂度 \(O(n)\)

所以这种贪心应该怎么想啊???

https://pjudge.ac/contest/1114/problem/21740

博弈的部分不写了,最后就是有一个函数长这样:

int query(int l, int r) {
    for (int i = 1; i <= k; i++) s[i] = 0;
    for (int i = l; i <= r; i++) s[a[i]]++;
    for (int i = k; i >= 2; i--) s[i - 1] += (s[i] + 1) / 2;
    return s[1];
}

不知道应该怎么注意到,这个东西等于 \(\lfloor\dfrac{2^k-1+\sum 2^{k-a_i}}{2^{k-1}}\rfloor\)。然后树状数组维护 \(\sum 2^{k-a_i}\) 就做完了。

posted @ 2025-11-10 16:12  Egg_eating_master  阅读(10)  评论(0)    收藏  举报