AGC013 部分题目题解

C - Ants on a Circle

把掉头视作穿过,然后容易得到最后每只蚂蚁的位置。只需要确定哪只蚂蚁是 \(1\) 号蚂蚁。
统计有多少只蚂蚁穿过了 \(x=M−0.5\) 这个位置即可。

D - Pilling Up

相当于取出来一个又放进去一个,不妨把这个过程刻画到平面图上,去掉首尾,那么就是统计只能向三个方向走,起点为 \((0, 0), (1, 0), \dots, (N - 1, 0)\),不能越过 \(y = 0\)\(y = N - 1\),形状不同的路径数量。
一个避免数重的简单方法是,枚举第一个碰到下边界的点,然后把两边的方案乘起来。

E - Placing Squares

嗯拆组合意义:

  • 有黑球、红球、蓝球三种球;
  • 每个位置上最多放一个黑球,有的位置不能放黑球。
  • 记两个相邻的黑球的位置分别为 \(x, y\)\(x < y\)),那么 \([x, y)\) 之间有恰好一个红球和一个蓝球。

记录所有可能的状态,使用矩阵快速幂优化转移即可。

F - Two Faced Cards

固定 \(Z\),用 Hall 定理刻画,把 \(Y\) 中的牌视为左部点,那么只需对排序后 \(Y\) 中的前缀检查。
每个 \(C_i\) 相当于给某个 \(S_i\)\([C_i, +\infty)\) 位置上都减了 \(1\),而如果钦定每个 \(Z\) 中的卡牌先选正面上的元素,并枚举选 \(D_i, E_i\),那么就继续转化为以下问题:

  • 给定若干区间 \([l_i, r_i]\)(每次询问都固定),每次可选择一些区间,把这些区间内的 \(S_i\)\(1\)(多个区间算多次),给定 \(k\),求使得 \(\forall i \le k\)\(S_i \ge 0\),且 \(\forall i > k\)\(S_i \ge -1\),最少操作多少个区间。

至此我们终于可以发现,可以运用贪心,从右往左扫描,找到第一个不合法位置 \(i\),并选择 \(l_P\) 最小且覆盖 \(i\) 的区间 \(P\)。观察问题形式,我们完全可以先令 \(k = 0\) 跑一次,再从小到大扫描 \(k\),使用堆可以简单维护。
你可能会疑惑为什么一定要从大到小跑,下面给出一个证明:

证明:对于某个 \(k\) 和一个合法解,我们找到区间 \(Q\),表示右端点最小的,且覆盖 \(i\) 的区间,进行分类讨论:

  • \(k > i\),则我们发现 \(Q\) 的右端点肯定会被另一个覆盖 \(i\) 的区间 \(R\) 覆盖,那么对 \(i \le j \le r_Q\)\(S_j\) 肯定大于 \(0\),直接将 \(Q\) 改为 \(P\) 不劣;
  • 否则,我们既然已经合法,就直接把 \(Q\) 改成 \(P\) 不劣。

其实整个过程就是,不断简化问题,转化为更简单的形式,转化完了就做完了,以后还是要多想啊!

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
	ll x = 0; int sig = 1; char c = getchar();
	while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
	while(isdigit(c)) x = (x << 3) + (x << 1) + (c ^ '0'), c = getchar();
	return x * sig;
}
void Write(ll x) {
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) Write(x / 10);
	putchar((x % 10) ^ 48);
}
const int N = 100005;
int n, m, _m, k, a[N], b[N], c[N], d[N], uc[N], s[N], ans[N];
pair<int, int> p[N], _p[N];
bool del[N];
void Quit() { int T = Read(); while(T--) printf("-1\n"); exit(0); }
int main() {
    int i; n = Read() + 1;
    for(i = 1; i < n; i++) a[i] = Read(), b[i] = Read();
    for(i = 1; i <= n; i++) uc[i] = c[i] = Read();
    sort(uc + 1, uc + n + 1), k = unique(uc + 1, uc + n + 1) - uc - 1;
    for(i = 1; i <= n; i++) s[lower_bound(uc + 1, uc + k + 1, c[i]) - uc]--;
    for(i = 1; i < n; i++) {
        a[i] = lower_bound(uc + 1, uc + k + 1, a[i]) - uc;
        b[i] = lower_bound(uc + 1, uc + k + 1, b[i]) - uc;
        s[a[i]]++;
        if(b[i] < a[i]) m++, p[m] = make_pair(a[i] - 1, b[i]);
    }
    for(i = 1; i <= k; i++) s[i] += s[i - 1];
    if(m) sort(p + 1, p + m + 1), reverse(p + 1, p + m + 1);
    int cur = 0, j = 1, rem = n - 1;
    priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
    for(i = k; i; i--) {
        cur += d[i];
        while(j <= m && p[j].first >= i) q.emplace(p[j].second, j), j++;
        while(s[i] + cur < -1) {
            if(q.empty() || q.top().first > i) Quit();
            d[q.top().first - 1]--, d[p[q.top().second].first]++;
            del[q.top().second] = true, q.pop(), rem--, cur++;
        }
    }
    for(i = k; i; i--) d[i] += d[i + 1], s[i] += d[i];
    ans[0] = rem, cur = 0, memset(d, 0, sizeof(d));
    for(i = 1; i <= m; i++) if(!del[i]) _p[++_m] = make_pair(p[i].second, p[i].first);
    if(_m) sort(_p + 1, _p + _m + 1);
    while(!q.empty()) q.pop();
    for(i = j = 1; i <= k; i++) {
        cur += d[i];
        while(j <= _m && _p[j].first <= i) q.emplace(-_p[j].second, j), j++;
        while(s[i] + cur < 0) {
            if(q.empty() || -q.top().first < i) break;
            d[-q.top().first + 1]--, d[_p[q.top().second].first]++;
            q.pop(), rem--, cur++;
        }
        if(s[i] + cur < 0) {
            while(i <= k) ans[i++] = -N * 2;
            break;
        }
        ans[i] = rem;
    }
    int T = Read();
    while(T--) {
        int x = Read(), y = Read();
        x = lower_bound(uc + 1, uc + k + 1, x) - uc - 1;
        y = lower_bound(uc + 1, uc + k + 1, y) - uc - 1;
        Write(max(-1, max(ans[x] + 1, ans[y]))), putchar('\n');
    }
}
posted @ 2026-05-24 23:52  Include_Z_F_R_qwq  阅读(3)  评论(0)    收藏  举报