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;
}
本文来自博客园,作者:cwkapn,转载请注明原文链接:https://www.cnblogs.com/cwkapn/p/19075020


浙公网安备 33010602011771号