1344 字
7 分钟
训练赛3

题目链接#

训练赛3

A. Submission Bait#

题解#

先考虑只有一种权值的情况。显然此时若 nn 为奇数,则先手必胜,否则先手必败。

然后考虑多种权值。发现若所有权值的出现次数均为偶数,则先手必败,否则先手仅需选择最大的出现次数为奇数的权值,即可令后手进入必败状态。

于是仅需检查是否有一种权值出现次数为奇数即可。

代码#

void idol_produce(int testCase) {
/*Code Here*/
int n;
cin >> n;
map<int, int> cnt;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
cnt[x]++;
}
if (any_of(cnt.begin(), cnt.end(), [](const pair<int, int> &x) {
return x.second % 2 == 1;
})) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}

B. Array Craft#

题解#

[y,x][y,x] 均为 11,从 y1,x+1y - 1, x + 1 开始,分别向左右构造成 1/1-1/1 交替的形式。

前缀和满足如下形式:

  • 1,0,1,0,1y,2x,1,0,1,...1,0,1,0,1_y,2_x,1,0,1,...

  • 1,0,1,0y,1x,0,1,...-1,0,-1,0_y,1_x,0,1,...

后缀和满足如下形式:

  • ...,0,1,2y,1x,0,1,0,1...,0,1,2_y,1_x,0,1,0,1

  • ...,1,0,1y,0x,1,0,1...,1,0,1_y,0_x,-1,0,-1

x,yx, y 均满足要求

代码#

void idol_produce(int testCase) {
/*Code Here*/
int n;
cin >> n;
int x, y;
cin >> x >> y;
vector<int> ans(n + 1);
for (int i = y; i <= x; i++) {
ans[i] = 1;
}
for (int i = x + 1; i <= n; i++) {
ans[i] = -ans[i - 1];
}
for (int i = y - 1; i >= 1; i--) {
ans[i] = -ans[i + 1];
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " \n"[i == n];
}
}

C. Mad MAD Sum#

题解#

打表发现在第二次操作后,数组一定单调递增,且除了最大的权值外每种权值至少出现 22 次,每次操作后,数组右移一位。

代码#

void idol_produce(int testCase) {
/*Code Here*/
int n;
cin >> n;
ll sum = 0;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int j = 1; j <= 2; j++) {
int tmp = 0;
map<int, int> mp;
for (int i = 1; i <= n; i++) {
sum += a[i];
mp[a[i]]++;
if (mp[a[i]] >= 2) {
tmp = max(tmp, a[i]);
}
a[i] = tmp;
}
}
ll tmp = 0;
for (int i = 1; i <= n; i++) {
tmp += a[i];
}
for (int i = n; i >= 1; i--) {
sum += tmp;
tmp -= a[i];
}
cout << sum << '\n';
}

D. Linear Maze#

题解#

当访问到普通房间时,步数 +1+ 1,如果访问到特殊房间,则步数 +1+ 1 后翻倍,显然访问到特殊房间后,回到该房间需要重新走相同的路程

代码#

void idol_produce(int testCase) {
/*Code Here*/
int n, k;
cin >> n >> k;
vector<int> a(n + 1);
for(int i = 1;i <= k; i ++){
int x;
cin >> x;
a[x] = 1;
}
ll ans = 0;
for(int i = 1;i <= n; i++){
ans ++;
if(a[i]) ans *= 2;
}
cout << ans << '\n';
}

E. Grid Puzzle#

题解#

显然,由于 2×22 \times 2 只能对相邻两行进行,对于 (i,i+1)(i, i + 1) 行,至多只进行 112×22 \times 2 操作,否则使用 22 次覆盖行操作一定更优。

故考虑 dpdp ,令 dpi,0/1/2dp_{i,0/1/2} 表示覆盖了前 ii 行,且第 ii 行用 2×22 \times 2 覆盖了第 i+1i + 1 行的 0/12/340 / 1-2/ 3-4 个格子的最小操作数

代码#

void idol_produce(int testCase) {
/*Code Here*/
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
vector<vector<int> > dp(n + 1, vector<int>(3, INF));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
if (a[i] == 0) dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]});
else dp[i][0] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + 1;
if (a[i] <= 2) {
dp[i][0] = min(dp[i][0], dp[i - 1][1]);
dp[i][1] = min({dp[i - 1][0], dp[i - 1][1], dp[i - 1][2]}) + 1;
} else if (a[i] <= 4) {
dp[i][1] = dp[i - 1][2] + 1;
dp[i][2] = dp[i - 1][1] + 1;
}
}
cout << min({dp[n][0], dp[n][1], dp[n][2]}) << '\n';
}

F. Catch the Mole#

题解#

对于树高根号分治,目标是把老鼠逼到一条链上,最后二分找到位置

  • 先对一个叶子节点进行 5000=70\sqrt{5000} = 70 次无效询问,这样所有树高小于 7070 的点必然不存在老鼠

  • 对于树高大于 7070 的,从根节点开始递归询问。对于一个节点的儿子,只有树高大于 7070 才被询问,小于 7070 的直接跳过即可

    询问为真或者只有一个儿子的时候就直接递归进去,为假就下一个儿子因此这样的询问最多 7070 次。

  • 最后二分即可完成,注意每次查询失败的时候,左右边界都要向根偏移一个位置

代码#

const int N = 70;
int ask(int x) {
cout << "? " << x << endl;
int res;
cin >> res;
return res;
}
# define answer(x) do { cout << "! " << x << endl; return;} while (0)
void idol_produce(int testCase) {
/*Code Here*/
int n;
cin >> n;
vector<vector<int>> e(n + 1);
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
vector<int> father(n + 1, 0), h(n + 1);
function<void(int, int)> dfs = [&](int u, int fa) -> void {
for (auto v: e[u]) {
if (v == fa) {
continue;
}
father[v] = u;
dfs(v, u);
h[u] = max(h[u], h[v] + 1);
}
};
dfs(1, 0);
int tmp = find(h.begin() + 1, h.end(), 0) - h.begin();
for (int i = 1; i <= N; i++) {
if (ask(tmp) == 1) {
answer(tmp);
}
}
function<int(int)> dfs2 = [&](int u) -> int {
vector<int> son;
for (auto v: e[u]) {
if (v == father[u] || h[v] < N) {
continue;
}
son.emplace_back(v);
}
if (son.empty()) {
return u;
}
for (auto x: son) {
if (x == son.back() || ask(x) == 1) {
return dfs2(x);
}
}
assert(false);
};
int end = dfs2(1);
vector<int> a;
for (int i = end; i != 0; i = father[i]) {
a.emplace_back(i);
}
reverse(a.begin(), a.end());
int l = 0, r = a.size() - 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (ask(a[mid]) == 1) {
l = mid;
} else {
r = mid - 1;
l = max(0, l - 1);
r = max(0, r - 1);
}
}
answer(a[l]);
}
训练赛3
https://fuyuki.fun/posts/题解/训练赛3/
作者
Fuyuki_Vila
发布于
2025-02-15
许可协议
CC BY-NC-SA 4.0