CodeForces Round #939(Div. 2) 补题记录(A~F)
A
因此这
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
int T;
cin >> T;
while (T--) {
int k, q;
cin >> k >> q;
for (int i = 1; i <= k; i++)
cin >> a[i];
while (q--) {
int x;
cin >> x;
cout << min(x, a[1] - 1) << ' ';
}
cout << '\n';
}
return 0;
}
B
因为先手和后手的石子的总数量相同,所以:
- 两张牌都在先手的牌的数量和两张牌都在后手的牌的数量相同。若两张牌都在先手则定义这张牌是 A 的,否则定义是 B 的。
- 剩下的牌一定一张在先手一张在后手。这样的牌定义为是 C 的。
因此若先手拿了 C 的牌,那么后手一定可以拿走这张牌所对应的那一张牌。因此此时先手并不能获取贡献,而后手可以得到一分。
而对于一张 A 的牌,先手若选取了一张 A 的牌拿走,那么后手因为没有 A 的牌所以无法拿走下一张 A 的牌,此时后手可以拿走任意一张 B 的牌。然后先手拿走另外一张 A 的牌,后手拿走另外一张 B 的牌。此时先手和后手的得分各增加一分。
最后一定只会剩下 C 的牌。这些牌对先手而言不会获得贡献。因此答案为所有不同的 A 的牌的的数量。
开一个桶来记录每一张牌,单组数据的时间复杂度为
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N], box[N];
signed main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
box[i] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
box[a[i]]++;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
if (box[i] == 2) cnt++;
cout << cnt << '\n';
}
return 0;
}
C
猜测最后构造的
经过多次尝试发现可以每一次先后给第
因此直接模拟即可。时间复杂度为
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[510][510];
signed main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
int cnt = 0, s = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j] = max(i, j), s += a[i][j];
cout << s << ' ' << 2 * n << '\n';
int idx = n;
for (int i = 1; i <= n; i++) {
cout << "1 " << idx << ' ';
for (int i = 1; i <= n; i++) cout << i << ' '; cout << '\n';
cout << "2 " << idx << ' ';
for (int i = 1; i <= n; i++) cout << i << ' '; cout << '\n';
idx--;
}
}
return 0;
}
D
首先考虑:对于
构造:
- 若
一段区间全部为 ,则已经达成条件。 - 否则,将所有
的 ,都让 这一段区间取 。
但是第二步最坏需要执行区间的长度次操作,太过于慢。所以考虑优化:
- 若
中没有 ,则对 整体执行一次 操作,全部变为 。 - 若
中有 ,则对 整体先执行一遍 操作,此时 区间中所有元素全部非 ,所以转化为第一种情况。
然后发现对于一个全
构造方案:
容易发现,若对于一段序列
所以说,若想要把
更具体的来说,考虑递归。设当前递归的区间是
- 若
:- 若
,则一次操作区间 让 。 - 若
,则两次操作区间 。其中第一次 ,第二次即转化为第一种 的情况。
- 若
- 若
:- 先让
区间全部归 。 - 将
区间递归处理,此时 区间的值为 。 - 对
区间整体做 操作。此时 区间的值是 。 - 对
整体做 操作,此时 区间的值是 。 - 对
区间递归处理,此时 区间的值是 。
- 先让
因此在最坏操作
最后整体对
现在考虑一种比较简单的方法:对
首先计算一段区间
- 若
,则最多执行 次操作。 - 若
,则:- 第一步,让区间
全部归零。操作最多执行 次。 - 第三、四步,让区间
和区间 分别整体做 操作,操作最多执行 次。
- 第一步,让区间
- 现在考虑对区间
的长度 从小到大讨论。- 若
即 最多执行 次操作。 - 若
则归 最多 步,此时两次递归处理的时候归 操作最多执行 次,因此最多执行 次操作。 - 若
则同理,最多执行 次操作。
- 若
- 以此类推。因而当
即区间 的长度为 的时候,最多执行的操作数量是 次。 - 因为
,所以单次区间执行 覆盖的次数最多为 次。但是容易发现这个方法很难使得 覆盖操作执行的次数卡的这么满,所以是可以在 次操作内得到答案的。
现在考虑证明多个区间的最多操作次数一定不会多于单个最大区间覆盖的区间的最多操作次数。
考虑将区间
又因为若将
因此就证明了上述命题。因此这样的构造方案不会超出构造次数的限制。
考虑转化原问题。因此原问题的求操作后数列的最大和问题就变为了:
给定长度为
的序列 。现在可以执行若干次操作,每一次操作可以选定一段区间 并将这段区间内所有的元素全部赋值为 。问最后 序列的最大和为多少。
这是一个很简单的 dp 问题。但是发现
若分离计算,则总的时间复杂度为
代码实现:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 18;
int a[N], b[N];
void mex(int l, int r) {
bool box[20] = {false};
for (int i = l; i <= r; i++)
if (a[i] <= 18)
box[a[i]] = true;
int val = 0;
while (box[val])
val++;
for (int i = l; i <= r; i++)
a[i] = val;
}
void clear(vector<pair<int, int>> &oper, int l, int r) {
if (accumulate(a + l, a + r + 1, 0ll) == 0)
return;
if (count(a + l, a + r + 1, 0ll) == 0) {
mex(l, r);
oper.push_back({l, r});
} else {
mex(l, r);
oper.push_back({l, r});
mex(l, r);
oper.push_back({l, r});
}
}
void fun(vector<pair<int, int>> &oper, int l, int r) {
if (l == r) {
clear(oper, l, r);
a[l] = 1;
return;
}
clear(oper, l, r);
fun(oper, l + 1, r);
mex(l, r);
oper.push_back({l, r});
mex(l, r - 1);
oper.push_back({l, r - 1});
fun(oper, l, r - 1);
}
signed main() {
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
int mx = accumulate(a, a + n, 0ll), id = 0;
for (int i = 1; i < (1ll << n); i++) {
for (int j = 0; j < n; j++)
b[j] = a[j];
vector<pair<int, int>> seg;
int l = -1, r = -1;
for (int j = 0; j < n; j++)
if (i >> j & 1) {
if (r == -1)
l = r = j;
else
r = j;
} else {
if (r != -1)
seg.push_back({l, r});
l = r = -1;
}
if (r != -1)
seg.push_back({l, r});
for (auto &[l, r] : seg)
for (int j = l; j <= r; j++)
b[j] = r - l + 1;
int now = accumulate(b, b + n, 0ll);
if (now > mx) {
mx = now;
id = i;
}
}
cout << mx << ' ';
if (!id) {
cout << "0 ";
} else {
vector<pair<int, int>> seg, oper;
int l = -1, r = -1;
for (int j = 0; j < n; j++)
if (id >> j & 1) {
if (r == -1)
l = r = j;
else
r = j;
} else {
if (r != -1)
seg.push_back({l, r});
l = r = -1;
}
if (r != -1)
seg.push_back({l, r});
for (auto &[l, r] : seg) {
fun(oper, l, r);
oper.push_back({l, r});
}
cout << oper.size() << '\n';
for (auto &[l, r] : oper)
cout << l + 1 << ' ' << r + 1 << '\n';
}
return 0;
}
E1
定义
首先可以发现若
证明:
发现
若
然后大胆猜测执行
证明:若存在一个
时间复杂度为
E2
发现
问题在于一个链上若有连续的四只怪物
注意:若连续的三个位置是
时间复杂度为
#include <bits/stdc++.h>
// #define int long long
using namespace std;
const int N = 500100;
int a[N], n;
int dist(int l, int r) {
if (l <= r)
return r - l + 1;
else {
r += n;
return r - l + 1;
}
}
int NXT(int x) {
if (x == n)
return 1;
return x + 1;
}
bool check() {
for (int i = 1; i <= n; i++)
if (a[i] && a[NXT(i)] && a[NXT(NXT(i))] && a[NXT(NXT(NXT(i)))])
return true;
return false;
}
int PRE(int x) {
if (x == 1)
return n;
return x - 1;
}
signed main() {
ios_base::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (n > 3) {
int W = *max_element(a + 1, a + n + 1);
while (check()) {
for (int j = 1; j <= n; j++)
a[j % n + 1] = max(a[j % n + 1] - a[j], 0);
}
vector<pair<int, int>> seg;
for (int i = 1; i <= n; i++)
if (a[i]) {
int p = i;
while (a[i]) i = i % n + 1;
seg.emplace_back(p, PRE(i));
if (p > i - 1)
break;
}
vector<int> arr;
for (auto &[l, r] : seg) {
if (a[PRE(l)])
continue;
if (dist(l, r) <= 2)
arr.emplace_back(l);
else {
arr.emplace_back(l);
if (l != n) {
int dl = a[NXT(l)] % a[l], dr = a[NXT(l)] - a[l];
if (!dl)
dl += a[l];
if (dl > dr)
arr.emplace_back(r);
else {
int dlen = (dr - dl) / a[l] + 1;
long long ds = 1ll * (dl + dr) * dlen / 2;
if (ds < a[r])
arr.emplace_back(r);
}
} else {
int dl = a[NXT(l)] % a[l], dr = a[NXT(l)];
if (!dl)
dl += a[l];
if (dl > dr)
arr.emplace_back(r);
else {
int dlen = (dr - dl) / a[l] + 1;
long long ds = 1ll * (dl + dr) * dlen / 2;
if (ds < a[r])
arr.emplace_back(r);
}
}
}
}
sort(arr.begin(), arr.end());
cout << arr.size() << '\n';
for (auto &x : arr)
cout << x << ' ';
cout << '\n';
} else if (n == 3) {
while (a[1] && a[2] && a[3]) {
a[2] = max(0, a[2] - a[1]);
a[3] = max(0, a[3] - a[2]);
a[1] = max(0, a[1] - a[3]);
}
vector<int> arr;
if (a[1] && !a[3])
arr.emplace_back(1);
if (a[2] && !a[1])
arr.emplace_back(2);
if (a[3] && !a[2])
arr.emplace_back(3);
cout << arr.size() << '\n';
for (auto &x : arr)
cout << x << ' ';
cout << '\n' ;
} else {
// n == 2
while (a[1] && a[2]) {
a[2] = max(0, a[2] - a[1]);
a[1] = max(0, a[1] - a[2]);
}
vector<int> arr;
if (a[1])
arr.emplace_back(1);
if (a[2])
arr.emplace_back(2);
cout << arr.size() << '\n';
for (auto &x : arr)
cout << x << ' ';
cout << '\n';
}
}
}
F
容易发现两个人
发现判断的式子均为
具体的说,对于每一个
然后考虑处理,分类讨论:
- 当前二元组的第二维是
,那么就将 push 进去。 - 当前二元组的第二维是
,那么就将 push 中所有第一维的值 的点向 连一条双向边。
答案就是新建立的图
但是建立的边的数量是
考虑优化。发现 push 集中若存在两个点
因此每一次连边的时候,对于图
时间复杂度为
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int N = 4000100;
int fa[N], l[N], r[N];
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
struct _ {
int x, y, id;
} z[N << 1];
bool operator<(const _ &l, const _ &r) {
return l.x < r.x || l.x == r.x && l.y < r.y;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
z[i * 2 - 1] = {i + l[i], 0, i};
z[i * 2] = {i - l[i], 1, i};
}
sort(z + 1, z + n + n + 1);
set<pair<int, int>> se;
for (int i = 1; i <= 2 * n; i++) {
if (z[i].y == 0)
se.insert({z[i].id + r[z[i].id], z[i].id});
else {
vector<int> v;
while (se.size() && (*se.rbegin()).first >= z[i].id - r[z[i].id]) {
v.push_back((*se.rbegin()).second);
se.erase(prev(se.end()));
}
if (v.size()) {
for (int j = 1; j < v.size(); j++) {
int a = v[j], b = v[j - 1];
int ta = find(a), tb = find(b);
if (ta != tb) fa[ta] = tb;
}
int a = z[i].id, b = v[0];
int ta = find(a), tb = find(b);
if (ta != tb) fa[ta] = tb;
se.insert({v[0] + r[v[0]], v[0]});
}
}
}
int cnt = 0;
for (int i = 1; i <= n; i++)
if (find(i) == i)
cnt++;
cout << cnt << '\n';
}
return 0;
}
本文来自博客园,作者:yhbqwq,转载请注明原文链接:https://www.cnblogs.com/yhbqwq/p/18137609,谢谢QwQ
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)