Featured image of post Hello 2026

Hello 2026

B

题目大意:给定一个长度为 $n$ 的数组 $a$,以及一个整数 $k$。令 $f(l, r)$ 表示 $\operatorname{mex}(a_l,a_{l+1},\ldots,a_r)$ $^\text{∗}$ 的值。需要进行如下操作 $n-k+1$ 次: - 设当前序列长度为 $|a|$。需要找到一个长度为 $k$ 的区间 $[l, r]$,使得 $\operatorname{max}_{i=1}^{|a|-k+1} f(i, i+k-1) = f(l, r)$。换句话说,需要在所有长度为 $k$ 的窗口中,选择一个 $\operatorname{mex}$ 最大的窗口 $[l, r]$。如果有多个符合条件的区间 $[l, r]$,可以任选其一。

数据范围:$1 \le t \le 10^4$,$2\leq k \leq n \leq 2\cdot 10^5$,$0\leq a_i \leq n$,$\sum n \le 2\cdot 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int i = 0; i <= n - 1; i++) cin >> a[i];
    map<int, int> ma;
    for (int p : a) ma[p]++;
    int cnt = 0;
    if (!ma.count(0)) {
        cout << 0 << endl;
        return;
    }
    for (auto [x, y] : ma) {
        if (x == cnt)
            cnt++;
        else
            break;
        if (cnt == k - 1) break;
    }
    cout << cnt << endl;
    return;
}

C

题目大意:战争爆发了!你作为国家的最高将领,必须制定战略部署你的军队。有 $n$ 个基地排成一行,第 $k$ 个基地是你的主基地。最开始,只有一个士兵驻扎在第 $k$ 个基地。每天按照如下顺序发生: - 你下达命令,选择一个基地 $i$($1 \leq i \leq n$),并选择该基地内任意数量的士兵(可以为 $0$,也可以为该基地全部士兵),然后命令这些士兵全部向相同方向移动:要么移动到 $i-1$ 号基地,要么移动到 $i+1$ 号基地。没有士兵能够移动到 $1$ 号基地的左侧或 $n$ 号基地的右侧。- 之后,会有一名新的士兵加入到第 $k$ 个基地。这名士兵不能被当天的命令调动。不过时间紧迫,距离敌军进攻只剩下 $m$ 天。

数据范围:$1 \le t \le 10^4$,$1 \leq k \leq n \leq 10^5$,$1 \leq m \leq 10^9$,$\sum n \le 2 \cdot 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    int l = k - 1, r = n - k;
    if (l > r) swap(l, r);
    if (max(2 * r - 1 + l, l + 2 * r - 1) <= m) {
        cout << n << endl;
        return;
    }
    int tem = 0;
    int tem2 = 0;
    int t = min(l, (m + 1) / 3);
    tem = t + 1 + min(r, (m + 1 - t) / 2);
    cout << tem << endl;
    return;
}

D1

题目大意:这是本题的 Easy 版本,两个版本的区别在于本版本只要求你求出最小操作次数。只有当你解决了所有版本后才能进行 Hack。给定一棵以 $1$ 号点为根的树 $^{\text{∗}}$,共 $n$ 个顶点,编号为 $1$ 到 $n$,每个顶点初始都是白色。定义 $d_i$ 为 $i$ 号顶点到根节点的距离。你可以执行任意次如下操作: 1. 选择一个白色顶点的子集 $S$,满足子集中没有两个节点有边直接连接,且没有两个节点到 $1$ 号节点的距离相等。形式化地说,对于 $S$ 中任意 $x,y$ 且 $x\ne y$,有 $d_x\ne d_y$,且 $x$ 和 $y$ 之间没有边直接连接。2. 将 $S$ 中的所有顶点染成黑色。

数据范围:$1\le t\le 10^4$,$2\le n\le 2\cdot 10^5$,$1\le u_i,v_i\le n$,$\sum n \le 2\cdot 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void solve() {
    int n;
    cin >> n;
    int x, y;
    vector<vector<int>> ma(n);
    for (int i = 0; i < n - 1; i++) {
        cin >> x >> y;
        ma[x - 1].push_back(y - 1);
        ma[y - 1].push_back(x - 1);
    }
    map<int, int> s;
    int maxx2 = INT_MIN;
    auto dfs = [&](this auto&& dfs, int x, int pa, int d) -> void {
        s[d]++;
        if (x == 0)
            maxx2 = max(maxx2, (int)ma[0].size() + 1);
        else
            maxx2 = max(maxx2, (int)ma[x].size());
        for (auto p : ma[x]) {
            if (p == pa) continue;
            dfs(p, x, d + 1);
        }
        return;
    };
    dfs(0, -1, 0);
    int maxx = INT_MIN;
    for (auto [x, y] : s) {
        maxx = max(maxx, y);
    }
    cout << max(maxx2, maxx) << endl;
    return;
}

D2

题目大意:这是该问题的 Hard 版本。不同之处在于,本题中你不仅需要求出最少操作次数,还需要输出一种达到该次数的染色方案。只有在你完成了所有版本的题目的情况下,才可以进行 Hack。给定一棵 $n$ 个顶点的有根树 $^{\text{∗}}$,顶点编号为 $1$ 到 $n$,根节点编号为 $1$,所有顶点初始时均为白色。定义 $d_i$ 为根到第 $i$ 个顶点的距离。你可以进行如下操作任意多次: 1. 选择一组白色顶点组成的集合 $S$,要求集合内任意两个节点都不通过边相连,且到根节点的距离均不相同。即对任意 $x, y \in S$ 且 $x \neq y$,有 $d_x \neq d_y$ 且 $x$ 和 $y$ 间没有直接的边相连。

数据范围:$1 \leq t \leq 10^4$,$2 \leq n \leq 2 \cdot 10^5$,$1 \leq u_i,v_i \leq n$,$\sum n \le 2 \cdot 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
void solve() {
    int n, x, y;
    cin >> n;
    vvi ma(n);
    rep(i, 1, n - 1) {
        cin >> x >> y;
        ma[x - 1].push_back(y - 1);
        ma[y - 1].push_back(x - 1);
    }
    map<int, int> s;
    map<int, map<int, vi>> s2;
    int maxx = INT_MIN;
    vector<pii> tem(n);
    vi depth(n);
    auto dfs = [&](this auto&& dfs, int x, int pa, int d) -> void {
        s[d]++;
        depth[x] = d;
        int cnt = 0;
        tem[x].first = pa;
        for (int& p : ma[x]) {
            if (p == pa) continue;
            cnt++;
            dfs(p, x, d + 1);
        }
        if (s.count(d + 1) && cnt == s[d + 1])
            maxx = max(maxx, cnt + 1);
        else
            maxx = max(maxx, cnt);
        return;
    };
    dfs(0, -1, 0);
    for (auto& [x, y] : s) {
        maxx = max(maxx, y);
    }
    cout << maxx << endl;
    int md = sz(s);
    vvi ma2(md);
    rep(i, 0, n - 1) ma2[depth[i]].push_back(i);
    tem[0].second = 0;
    rep(i, 1, md - 1) {
        sort(all(ma2[i]), [&](const int& x, const int& y) { return tem[tem[x].first].second < tem[tem[y].first].second; });
        vector<bool> vis(maxx, false);
        rep(j, 0, sz(ma2[i]) - 1) { vis[(tem[tem[ma2[i][j]].first].second - j + maxx) % maxx] = true; }
        int idx = 0;
        while (vis[idx]) idx++;
        rep(j, 0, sz(ma2[i]) - 1) { tem[ma2[i][j]].second = (j + idx + maxx) % maxx; }
    }
    vvi res(maxx);
    rep(i, 0, n - 1) { res[tem[i].second].push_back(i); }
    for (auto& p : res) {
        cout << sz(p) << endl;
        for (auto& q : p) cout << q + 1 << ' ';
        cout << endl;
    }
    return;
}

E

题目大意:给定一个长度为 $n$ 的序列 $a$ 和一个正整数 $m$。序列 $a$ 的每个元素都是 $[0, m]$ 范围内的整数。当且仅当以下两个条件都满足时,序列 $a$ 被认为是好的: - $a_1 < a_2 < a_3 < \ldots < a_n$; - $\frac{1}{\operatorname{lcm}(a_1,a_2)}+\frac{1}{\operatorname{lcm}(a_2,a_3)}+\ldots+ \frac{1}{\operatorname{lcm}(a_{n-1},a_n)}+{\color{red}\frac{1}{\operatorname{lcm}(a_n,a_1)}}\ge 1$。

数据范围:$1 \le t \le 1000$,$2 \le n \le m \le 3000$,$0 \le a_i \le m$,$\sum m \le 3000$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
constexpr int MOD = 998244353;
constexpr int MX = 1e5 + 1;
ll F[MX];      // 预处理阶乘
ll INV_F[MX];  // 预处理逆元
ll qpow(ll x, int n) {
    ll res = 1;
    for (; n; n >>= 1) {
        if (n % 2) res = res * x % MOD;
        x = x * x % MOD;
    }
    return res;
}
auto init = [] {
    F[0] = 1;
    for (int i = 1; i < MX; i++) F[i] = F[i - 1] * i % MOD;  // 预处理阶乘
    INV_F[MX - 1] = qpow(F[MX - 1], MOD - 2);
    for (int i = MX - 1; i; i--) {
        INV_F[i - 1] = INV_F[i] * i % MOD;
    }  // 预处理逆元
    return 0;
}();
// 计算C(n,m),即从n个数中取m个数
ll comb(int n, int m) { return m < 0 || m > n ? 0 : F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD; }
void solve() {
    int n, m;
    cin >> n >> m;
    auto lcm = [&](int x, int y) {
        int tem = __gcd(x, y);
        return x / tem * y;
    };
    vi a(n);
    rep(i, 0, n - 1) cin >> a[i];
    if (a[0] != 0 && a[0] != 1) {
        cout << 0 << endl;
        return;
    }
    if (a[1] != 0 && a[1] != 2) {
        cout << 0 << endl;
        return;
    }
    a[0] = 1;
    a[1] = 2;
    vvi dp(n, vi(m + 1));
    ll ans = 0;
    dp[0][1] = 1;
    dp[1][2] = 1;
    rep(i, 2, n - 1) {
        if (a[i] == 0) {
            rep(j, 1, m) {
                for (int k = 1; (k + 1) * j <= m; k++) {
                    dp[i][(k + 1) * j] += dp[i - 1][k * j] % MOD;
                    dp[i][(k + 1) * j] %= MOD;
                }
            }
        } else {
            rep(j, 1, a[i] - 1) {
                if (__gcd(a[i], j) == a[i] - j) {
                    dp[i][a[i]] += dp[i - 1][j] % MOD;
                    dp[i][a[i]] %= MOD;
                }
            }
        }
    }
    rep(i, 1, m) {
        ans += dp[n - 1][i] % MOD;
        ans %= MOD;
    }
    cout << ans << endl;
    return;
}