A. Submission Bait (思维)
题意:
AliceAliceAlice 和 BobBobBob 正在玩一个数组 aaa 中大小为 nnn 的游戏。
他们轮流进行操作,AliceAliceAlice先开始。无法操作的玩家将输掉。首先,将变量 mxmxmx 设置为 000 。
在一次操作中,玩家可以执行以下操作:
- 选择一个索引 iii ( 1≤i≤n1 \le i \le n1≤i≤n ),使得 ai≥mxa_{i} \geq mxai≥mx ,并将 mxmxmx 设置为 aia_{i}ai 。然后,将 aia_{i}ai 设置为 000 。
确定AliceAliceAlice是否有获胜策略。
分析:
考虑最大值的奇偶性,如果有奇数个最大值,那么先手赢。如果是偶数个最大值,那么就要考虑剩下的数字的奇偶性。如果有一个值有奇数个,那么先手赢,否则后手赢
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t = 1;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 0; i < n; i++) {
int x;
cin >> x;
a[x]++;
}
int flag = 0;
for (int i = n; i; i--) {
if (a[i] & 1) {
flag = 1;
break;
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
B.Array Craft (思维)
题意:
对于大小为 mmm 的数组 bbb ,我们定义:
- bbb 的最大前缀位置是满足 b1+…+bi=maxj=1m(b1+…+bj)b_1+\ldots+b_i=\max_{j=1}^{m}(b_1+\ldots+b_j)b1+…+bi=maxj=1m(b1+…+bj) 的最小索引 iii ;
- bbb 的最大后缀位置是满足 bi+…+bm=maxj=1m(bj+…+bm)b_i+\ldots+b_m=\max_{j=1}^{m}(b_j+\ldots+b_m)bi+…+bm=maxj=1m(bj+…+bm) 的最大索引 iii 。
给出三个整数 nnn 、 xxx 和 yyy ( x>yx > yx>y )。构造一个大小为 nnn 的数组 aaa ,满足:
- 对于所有 1≤i≤n1 \le i \le n1≤i≤n , aia_iai 为 111 或 −1-1−1 ;
- aaa 的最大前缀位置为 xxx ;
- aaa 的最大后缀位置为 yyy 。
如果有多个数组满足条件,则输出任意一个。可以证明,在给定条件下,这样的数组始终存在。
分析:
我们让yyy的左侧−1,1-1,1−1,1交替出现,在区间[y,x][y,x][y,x]全选择111,xxx的右侧也是−1,1-1,1−1,1交替出现即可完成构造。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t = 1;
cin >> t;
while (t--) {
int n, x, y;
cin >> n >> x >> y;
vector<int> ans(n + 1, 1);
for (int i = y - 1; i >= 1; i--) {
ans[i] = -ans[i + 1];
}
for (int i = x + 1; i <= n; i++) {
ans[i] = -ans[i - 1];
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " ";
}
cout << endl;
}
return 0;
}
C.Mad MAD Sum (思维)
题意:
我们将数组中的 MAD{MAD}MAD (最大重复数)定义为数组中至少出现两次的最大数字。具体来说,如果没有至少出现两次的数字,则 MAD{MAD}MAD 的值是 000 。
例如, MAD([1,2,1])=1{MAD}([1, 2, 1]) = 1MAD([1,2,1])=1 、 MAD([2,2,3,3])=3{MAD}([2, 2, 3, 3]) = 3MAD([2,2,3,3])=3 、 MAD([1,2,3,4])=0{MAD}([1, 2, 3, 4]) = 0MAD([1,2,3,4])=0 。
给定一个大小为 nnn 的数组 aaa 。最初,将变量 sumsumsum 设置为 000 。
以下过程将以顺序循环执行,直到 aaa 中的所有数字都变为 000 :
- 设置 sum:=sum+∑i=1naisum := sum + \sum_{i=1}^{n} a_isum:=sum+∑i=1nai ;
- 让 bbb 成为大小为 nnn 的数组。将所有 1≤i≤n1 \le i \le n1≤i≤n 设置为 bi:= MAD([a1,a2,…,ai])b_i :=\ {MAD}([a_1, a_2, \ldots, a_i])bi:= MAD([a1,a2,…,ai]) ,然后将所有 1≤i≤n1 \le i \le n1≤i≤n 设置为 ai:=bia_i := b_iai:=bi 。
求出经过此处理后的 sumsumsum 的值。
分析:
发现从第二次操作开始,数列一定为单调递增,且除了最大的权值外每种权值至少出现222次,且每次操作对数列的影响均为使数列整体右移一位,删去最后一个数并在前面补000。
于是我们先暴力模拟两轮记录贡献和,然后求得每种权值的出现次数,再降序枚举每种权值(也即每轮删数的顺序),统计删去该权值的各轮的贡献之和即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t = 1;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<LL> a(n), b(n);
LL ans = 0;
map<LL, LL> mp;
LL mx = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
ans += a[i];
mp[a[i]]++;
if (mp[a[i]] >= 2 && a[i] > mx) {
mx = a[i];
}
b[i] = mx;
}
mp.clear();
mx = 0;
for (int i = 0; i < n; i++) {
ans += b[i];
mp[b[i]]++;
if (mp[b[i]] >= 2 && b[i] > mx)
mx = b[i];
a[i] = mx;
}
for (int i = 0; i < n; i++)
ans += a[i] * (n - i);
cout << ans << endl;
}
return 0;
}
D.Grid Puzzle (dp)
题意:
您将获得一个大小为 nnn 的数组 aaa 。
有一个 n×nn \times nn×n 网格。在 iii 行中,前 aia_iai 个单元格为黑色,其他单元格为白色。换句话说,请注意 (i,j)(i,j)(i,j) 是 iii行和 jjj列中的单元格,单元格 (i,1),(i,2),…,(i,ai)(i,1), (i,2), \ldots, (i,a_i)(i,1),(i,2),…,(i,ai) 为黑色,单元格 (i,ai+1),…,(i,n)(i,a_i+1), \ldots, (i,n)(i,ai+1),…,(i,n) 为白色。
你可以按任意顺序多次执行以下操作:
- 将 2×22 \times 22×2 子网格染成白色;
- 将整行染成白色。请注意,你不能 将整列染成白色。
找出将所有单元格染成白色的最少操作次数。
分析:
我们发现,在每一行最多用一次2×22 \times 22×2的操作,因为超过111次,不如直接两次操作涂完两列。那么每一行最多被两个2×22 \times 22×2的操作影响(前一行和本行),并且可以看出,这个操作要么用在第1,21,21,2列,要么用在3,43,43,4列。所以我们维护以下333种dpdpdp值。
- 前一行是涂一整行。
- 前一行在第1,21,21,2列使用了2×22 \times 22×2操作。
- 前一行在第3,43,43,4列使用了2×22 \times 22×2操作。
进行转移即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
int main() {
int t = 1;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
int dp[3]{0, INF, INF}, tmp[3];
for (int i = 0; i < n; i++) {
memset(tmp, 0x3f, sizeof tmp);
tmp[0] = *min_element(dp, dp + 3) + (a[i] > 0);
if (a[i] <= 2) {
tmp[0] = min(tmp[0], dp[1]);
tmp[1] = min(tmp[1], dp[0] + 1);
}
if (a[i] <= 4) {
tmp[2] = min(tmp[2], dp[1] + 1);
tmp[1] = min(tmp[1], dp[2] + 1);
}
swap(dp, tmp);
}
cout << *min_element(dp, dp + 3) << endl;
}
return 0;
}
E2.Catch the Mole(Hard Version) (交互)
题意:
这是一个交互题。
给你一个节点数为 nnn 的树,节点 111 是它的根节点。
其中一个节点中隐藏着一只鼹鼠。要找到它的位置,你可以选择一个整数 xxx ( 1≤x≤n1 \le x \le n1≤x≤n ) 来向陪审团询问。接下来,当鼹鼠在子树 xxx 中时,陪审团将返回 111 。否则,法官将返回 000 。如果法官返回 000 并且鼹鼠不在根节点 111 中,鼹鼠将移动到它当前所在节点的父节点。
最多使用 160160160 次操作来找到鼹鼠所在的当前节点。
分析:
我们每次找出当前询问点子树的重心,这样就能一次排除尽量多的点,但如果出现类似菊花图之类的数据,是难以找到一个较为平衡的点,但要卡掉上述过程的图恰好需要满足点的分布较为均衡,那么此时子树的深度一定不大,很容易将目标点直接跳到根节点。
所以我们可以先在子树内每次选择重心缩小范围,最后留出一部分询问在当前点到根的路径上确定点的位置即可,因为可能这个点之前还在我们确定的子树里,几次操作后又跑到外面去了,所以要最后确定一下位置。由于这部分是一条链,因此可以二分处理,实测可以在160160160的限制内通过此题.
代码:
#include <bits/stdc++.h>
using namespace std;
inline bool qry(int x) {
cout << "? " << x << endl;
int s;
cin >> s;
return s;
}
vector<int> s[5005], e[5005];
int sz[5005], f[5005], le, rt;
void dfs(int x, int fa) {
sz[x] = 1;
f[x] = fa;
for (auto u: e[x]) {
if (u == fa)
continue;
s[x].push_back(u);
dfs(u, x);
sz[x] += sz[u];
}
if (e[x].size() == 1 && x != 1)
le = x;
if (sz[x] >= 71 && !rt) {
if (qry(x)) {
rt = x;
}
sz[x] = 0;
}
}
void solve() {
int n;
cin >> n;
rt = 0;
for (int i = 1; i <= n; i++) {
e[i].clear();
s[i].clear();
}
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
if (rt == 0)
rt = 1;
for (int i = 1; i < 71; i++) {
if (qry(le)) {
cout << "! " << le << "\n";
return;
}
}
vector<int> v;
while (rt)
v.push_back(rt), rt = f[rt];
int l = 0, r = v.size() - 1;
reverse(v.begin(), v.end());
while (l < r) {
int mid = l + r + 1 >> 1;
if (qry(v[mid]))
l = mid;
else {
r = mid - 1;
l--;
r--;
}
if (l < 0)
l = 0;
}
cout << "! " << v[l] << endl;
}
int main() {
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

686



被折叠的 条评论
为什么被折叠?



