Codeforces Round 967 (Div. 2) ABCD
来源:Codeforces Round 967 (Div. 2)
做题时间:2024_08_21
A. Make All Equal#
使所有元素相等的最小操作次数,就是保留最大的数字
所以操作次数就是总数减去数量最多得数
B. Generate Permutation#
题意#
构造一个序列
使长度为
要求用两台打字机时的
思路#
思路就是先写几个序列出来
比如
至于奇数怎么构造,还是用
那就先搞个
从后往前和从前往后都是
C. Guess The Tree#
题意#
这是一个[[交互型问题]],通过询问至多
思路#
我们可以根据返回值判断两个结点是不是直接相连,比如
如果是就存储起来
如果不是,设返回值是
我认为本题难度主要在于怎么组织代码
我是用一个
用一个队列存储上述的
代码#
#include <bits/stdc++.h>
using namespace std;
#define YES "Yes"
#define NO "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (int i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>
signed main() {
IOS;
int t = 0;
srand(time(0));
cin >> t;
while (t--) {
int n, num;
cin >> n;
queue<pii> pq; // 存查询
set<pii> st; // 存答案
map<int, int> mp; // 存点是否已被加入图中
map<pii, int> mp_find; // 存是否查询过,也可以用set
int root = rand() % n + 1;
while (st.size() != n - 1) {
if (pq.empty()) {
for (int i = 1; i <= n; i++) {
if (i != root && mp[i] == 0) {
pq.push({min(root, i), max(root, i)});
break;
}
}
}
while (pq.size()) {
int x = pq.front().first;
int y = pq.front().second;
pq.pop();
if (mp_find[{min(x, y), max(x, y)}] != 0) continue;
cout << "? " << x << " " << y << endl;
cout.flush();
cin >> num;
mp_find[{min(x, y), max(x, y)}] = num;
if (num != x) {
pii p1 = {min(x, num), max(x, num)};
pii p2 = {min(y, num), max(y, num)};
if (st.find(p1) == st.end()) pq.push(p1);
if (st.find(p2) == st.end()) pq.push(p2);
} else {
pii p1 = {min(x, y), max(x, y)};
if (st.find(p1) == st.end()) {
mp[x]++;
mp[y]++;
st.insert(p1);
}
}
}
}
cout << "! ";
for (auto &[x, y] : st) cout << x << " " << y << " ";
cout << endl;
cout.flush();
}
return 0;
}
D. Longest Max Min Subsequence#
题意#
找最长的不存在重复元素的子序列并输出
要求有多个的话,按照奇数位乘
就是奇数位尽量大,偶数位尽量小
思路#
获取子序列的长度很简单,用一个
假定子序列长度为
以样例
第一次选择只能在前三个之间选,因为假定选了第四个数字
如果我们把
对于可供选择的区间,区间的左边界由于子序列的原因只能是不减的
区间的右边界由于子序列长度的原因,也是不减的
那么我们只要根据当前是奇数位还是偶数位,在可供选择的区间里选择最大或是最小值就行
就是双指针的思路,用[[multiset]]维护区间,一旦某个数字被选择了,就是删除set内的所有当前数字和当前数字之前的数字,然后再根据每个数字出现的最后位置更新一下set的右区间
代码#
#include <bits/stdc++.h>
using namespace std;
#define YES "Yes"
#define NO "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (int i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>
const int N = 3e5 + 10;
signed main() {
IOS;
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vi a(n);
map<int, bool> vis;
map<int, int> index;
priority_queue<pii, vector<pii>, greater<pii>> Lx;// 存储每个数字出现的最后位置
multiset<int> ms;
rep(i, 0, n) {
cin >> a[i];
index[a[i]] = i;
}
for (auto &[x, in] : index) {
vis[x] = false;
Lx.push({in, x});
}
int len = index.size();
cout << index.size() << endl;
int be = 0;// begin
int en = 0;// end
int nlen = 0;
int num = -1;
while (nlen < len) {
while (vis[Lx.top().S]) Lx.pop();
for (int i = en; i <= Lx.top().F; i++) {
if (!vis[a[i]])
ms.insert(a[i]);
}
en = Lx.top().F + 1;
if (nlen % 2 == 0) {
num = *ms.rbegin();
cout << num << " ";
vis[num] = 1;
ms.erase(num);
nlen++;
} else {
num = *ms.begin();
cout << num << " ";
vis[num] = 1;
ms.erase(num);
nlen++;
}
for (int i = be;; i++) {
if (ms.find(a[i]) != ms.end())
ms.erase(ms.find(a[i]));
if (a[i] == num) {
be = i + 1;
break;
}
}
}
cout << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)