Codeforces Round 960 (Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/1990
依旧是红温温温温温温温温温温场。
AB 每题都吃了两发,爽!D 到最后也没调出来赛后五分钟看出来哪挂了,爽!又要掉一百分了,爽!
所有错误都是每题多看两眼就能找到的但就是没多看两眼。
妈的傻逼一天天的在这干啥啊、、、急成这个逼样子还是急着投胎算了。
A
手玩。
操作等价于选择某个权值的第一个数,并将在此之前所有数全部删除。
先考虑只有一种权值的情况。显然此时若 \(n\) 为奇数,则先手必胜,否则先手必败。
然后考虑多种权值。发现若所有权值的出现次数均为偶数,则先手必败,否则先手仅需选择最大的出现次数为奇数的权值,即可令后手进入必败状态。
于是仅需检查是否有一种权值出现次数为奇数即可。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
int n; std::cin >> n;
int cnt[100] = {0};
for (int i = 1; i <= n; ++ i) {
int a; std::cin >> a;
++ cnt[a];
}
int flag = 0;
for (int i = n; i; -- i) {
if (cnt[i] % 2 == 1) flag = 1;
}
std::cout << (flag ? "YES\n" : "NO\n");
}
return 0;
}
B
构造。
显然应令 \([y, x]\) 内全部为 1,\(y - 1, x + 1\) 均为 -1。然后考虑令 \([1, y - 1]\) 和 \([x + 1, n]\) 的前后缀和均尽可能地小以符合题意。
发现仅需从 \(y - 1, x + 1\) 开始,分别向左右构造成 -1/1 交替的形式即可。此时 \([1, y - 1]\) 和 \([x + 1, n]\) 的任意前后缀和均不大于 1。且若 \([1, y - 1]\) 存在前缀和为 1 则 \([1, y-1]\) 和为 0;若 \([x+1, n]\) 存在后缀和为 1 则 \([x+1, n]\) 和为 0。而在 \([y, x]\) 内至少会获得 +2 的贡献,则上述构造一定合法。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, x, y, ans[kN];
//=============================================================
//=============================================================
int main() {
//freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n >> x >> y;
// int a = x - 1, b = y - x - 1, c = n - x;
ans[x] = ans[y] = 1;
for (int i = y; i <= x; ++ i) ans[i] = 1;
for (int i = y - 1, j = -1; i; -- i, j = -j) ans[i] = j;
for (int i = x + 1, j = -1; i <= n; ++ i, j = -j) ans[i] = j;
for (int i = 1; i <= n; ++ i) std::cout << ans[i] << " ";
std::cout << "\n";
}
return 0;
}
C
模拟,暴力。
先手玩一下。前两次操作没什么规律,但发现从第二次操作开始,数列一定为单调递增,且除了最大的权值外每种权值至少出现 2 次,且每次操作对数列的影响均为使数列整体右移一位,删去最后一个数并在前面补 0。
于是先暴力模拟两轮记录贡献和,然后求得每种权值的出现次数,再降序枚举每种权值(也即每轮删数的顺序),统计删去该权值的各轮的贡献之和即可。
总时间复杂度 \(O(n)\) 级别。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN], cnt[kN];
LL ans;
//=============================================================
void solve() {
for (int i = 0; i <= n; ++ i) cnt[i] = 0;
int mad = 0;
for (int i = 1; i <= n; ++ i) {
ans += a[i];
++ cnt[a[i]];
if (cnt[a[i]] >= 2 && a[i] > mad) mad = a[i];
a[i] = mad;
}
}
LL sum(int L_, int R_) {
return 1ll * (L_ + R_) * (R_ - L_ + 1ll) / 2ll;
}
void solve1() {
LL delta = 0;
for (int i = 0; i <= n; ++ i) cnt[i] = 0;
for (int i = 1; i <= n; ++ i) delta += a[i], ++ cnt[a[i]];
for (int i = n; i; -- i) {
if (cnt[i] == 0) continue;
ans += 1ll * cnt[i] * delta - sum(1, cnt[i] - 1) * i;
delta -= 1ll * cnt[i] * i;
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
ans = cnt[0] = 0;
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
cnt[i] = 0;
}
solve();
solve();
solve1();
std::cout << ans << "\n";
}
return 0;
}
/*
1
3
2 2 3
*/
D
手玩,贪心。
发现使用 \(2\times 2\) 矩阵的操作 1 大部分情况下是不值的,对某两行使用两次操作 1 一定不优于两次操作 2。
手玩下可以发现如下结论:
- 使用一次操作 2 等于对下方所有行消除之前进行的操作 1 的影响,于是仅需考虑每次连续使用操作 1 的影响。
- 使用操作 1 被覆盖的行黑色数量一定不会大于 4,否则不优。
- 连续使用操作 1 时,若使用了 \(k\) 次则一定会使 \(k+1\) 行被全部覆盖,否则不优。
- 连续使用操作 1 时,被覆盖的第一行黑色数量一定不会大于 2,否则不优。
- 由结论 1234,连续使用操作 1 时,第一次操作 1 覆盖的范围一定是 \((x, 1), (x, 2)\),第二次一定是 \((x+1, 3), (x+1, 4)\),第三次一定是 \((x+2, 1), (x+2, 2)\),……直至第 \(x+k\) 行黑色数量大于 4,或使用操作 1 后无法覆盖到第 \(x+k+1\) 行,此时对第 \(x+k\) 行进行一次操作 2 一定不会更劣。然后从第 \(x+k+1\) 行开始重复上述过程。
根据结论 45,仅需从第一行开始贪心地操作即可。若某行满足进行第一次操作 1 的条件,则连续进行操作 1 直至不满足条件,再换用一次操作 2,再从下一行开始重复上述过程。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN];
//=============================================================
int solve(int now_, int left_, int right_) {
if (now_ > n) return 0;
if (a[now_] <= 0) return solve(now_ + 1, 0, 0); //此行不需操作
if (a[now_] <= 2) {
if (left_) return solve(now_ + 1, 0, 0); //左侧两个已被覆盖,则不需操作此行
return solve(now_ + 1, 1, 0) + 1; //对此行进行操作 1,不会更劣
}
if (a[now_] <= 4) {
if (right_) return solve(now_ + 1, 1, 0) + 1; //右侧两个已被覆盖,对左侧操作
if (left_) return solve(now_ + 1, 0, 1) + 1; //左侧两个已被覆盖,对右侧操作
//若无覆盖,对此行操作 2,不会更劣。
}
return solve(now_ + 1, 0, 0) + 1; //此行操作 2
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n;
for (int i = 1; i <= n; ++ i) std::cin >> a[i];
std::cout << solve(1, 0, 0) << "\n";
}
return 0;
}
/*
1
4
3 2 1 0
1
4
2 4 4 2
1
3
2 3 5
1
6
2 4 4 4 4 2
*/
E
交互,
写在最后
学到了什么:
- 急你马呢
- 急你马呢急你马呢
- 急你马呢急你马呢急你马呢
- 急你马呢急你马呢急你马呢急你马呢
- 急你马呢急你马呢急你马呢急你马呢急你马呢
- 急你马呢急你马呢急你马呢急你马呢急你马呢急你马呢
- 急你马呢急你马呢急你马呢急你马呢急你马呢急你马呢急你马呢
我操不想活了。