Codeforces 2133 记录

目录

  • 综述
  • C The Nether
  • D Chicken Jockey
  • E I Yearned For The Mines
  • F Flint and Steel

综述

这是一套 Div.2。

C The Nether

题目描述

交互题。多测 \(1000\) 组。有一个 DAG 有 \(n\) 个点,你需要找到一条经过尽可能多位置的路径。你的程序可以提问或回答问题:

  • \(?\ x\ k\ S_1\ S_2\cdots S_k\):询问这个 DAG 上,从 \(x\) 出发,仅经过 \(S\) 集合中的点能走的最大路径长度。你需要保证 \(x\in S\)
  • \(!\ k\ v_1\ v_2\cdots v_k\):回答问题,你的答案是从 \(v_1\)\(v_2\)\(v_3\) ……最终到 \(v_k\)

你可以提问 \(2\times n\) 次。\(n\le 500\)\(\sum n^3\le 1.25\times 10^8\)

解题思路

首先通过 \(n\) 次询问 \(?\ i\ n\ 1\ 2\ 3\dots n\) 得到以每个点为起点的最长路径长度 \(p_i\)。此时 \(p_i\) 最大的 \(i\) 就是起点。于是有 \(k=p_b\)

接下来考虑哪些点可以作为 \(v_2\)。注意到,点 \(i\) 可以作为 \(v_2\) 的充分条件是 \(p_i+1=p_b\)\(i\)\(b\) 相连。因此找到 \(p_i=p_b-1\) 的点 \(j\) 并询问 \(?\ b\ 2\ b\ j\) 来得知 \(b\)\(j\) 之间的连边情况。\(v_x\)\(x>2\))的情况同法可得。

这样做,求 \(p\) 数组共询问 \(n\) 次,求 \(v_x\)\(x>1\))的过程中除 \(b\) 外的每个点被询问 \(1\) 次,共询问 \(2\times n-1\) 次,满足题目要求。

代码

#include <bits/stdc++.h>
using namespace std;
#define rep(aaa, bbb, ccc) for (int aaa = (bbb); i <= (ccc); i++)
const int N = 505;
int n, p[N], v[N];
int main() {
    int _;
    cin >> _;
    while (_--) {
        cin >> n;
        memset(v, 0, sizeof(v));
        for (int i = 1; i <= n; i++) {
            cout << "? " << i << ' ' << n;
            for (int j = 1; j <= n; j++) cout << ' ' << j;
            cout << endl;
            cin >> p[i];
            if (p[i] >= p[v[1]]) v[1] = i;
        }
        int now = v[1];
        for (int i = 1; i < p[v[1]]; i++) {
            for (int j = 1; j <= n; j++) {
                if (p[j] != p[now] - 1) continue;
                cout << "? " << now << " 2 " << j << ' ' << now << endl;
                int x;
                cin >> x;
                if (x == 2) {
                    now = v[i + 1] = j;
                    break;
                }
            }
        }
        cout << "! " << p[v[1]];
        for (int i = 1; i <= p[v[1]]; i++) cout << ' ' << v[i];
        cout << endl;
    }
    return 0;
}

D Chicken Jockey

题目描述

Steve 做出了一个愚蠢的决定,在夜晚进行挖矿,结果遇到了一种可怕的生物:chicken jockey$$^n$$!

一个 chicken jockey$$^n$$ 由 $$n$$ 个生物依次叠在一起组成,第 $$\(1\)$$ 个生物在最底层,第 $$\(n\)$$ 个生物在最顶层。第 $$\(i\)$$ 个生物初始拥有 $$\(h_i\)$$ 点生命值。

每次攻击,Steve 可以对任意一个生物造成 \(1\) 点伤害。如果某个生物的生命值降到 \(0\) 或更低,则它会死亡,其上方的所有生物会掉落下来,重新组成一个新的堆叠。新堆叠中最底层的生物会因为掉落受到等于它之前下方生物数量(包括刚刚死亡的那个)的 \(1\) 点掉落伤害。如果这也导致它死亡,则其上方的所有生物再次掉落,过程重复进行。

例如,考虑一个初始生命值为 \([1, 2, 1, 3, 5, 2]\) 的 chicken jockey\(^6\)。如果 Steve 攻击堆叠中的第三个生物,它会死亡,生命值为 \([3, 5, 2]\) 的生物会掉落组成新堆叠。新堆叠的最底层生物会受到 \(3\) 点掉落伤害,因此也会死亡,生命值为 \([5, 2]\) 的生物再次掉落组成新堆叠。新堆叠的最底层生物会受到 \(1\) 点掉落伤害。最终,Steve 第一次攻击后,会剩下两个堆叠,生命值分别为 \([1, 2]\)\([4, 2]\)

Steve 的剑耐久度很低,他想知道消灭所有生物所需的最少攻击次数。

解题思路

\(f_i\) 为消灭前 \(i\) 个的最小次数。显然 \(f_1=a_1\)。对于 \(f_i\)\(i>1\)):

  • 先消灭前 \(i-1\) 个,此时第 \(i\) 个会收到 \(1\) 点伤害,然后消灭第 \(i\) 个:\(a_i-1+f_{i-1}\)
  • 先消灭第 \(i-1\) 个,此时第 \(i\) 个会收到 \(i-1\) 点伤害,然后消灭剩下的:\(a_{i-1}+(a_i-i+1)+f_{i-2}\)

注意,如果转移时某个生物的生命值会被扣成负数,应当特判。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n, a[N], f[N];
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int _;
    cin >> _;
    while (_--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        f[1] = a[1];
        for (int i = 2; i <= n; i++) f[i] = min(a[i] - 1 + f[i - 1], a[i - 1] + max(0ll, a[i] - i + 1) + f[i - 2]);
        cout << f[n] << '\n';
    }
    return 0;
}
posted @ 2025-09-05 12:50  cwkapn  阅读(13)  评论(0)    收藏  举报