Codeforces Round 967 (Div. 2) ABCD

来源:Codeforces Round 967 (Div. 2)

做题时间:2024_08_21

A. Make All Equal

使所有元素相等的最小操作次数,就是保留最大的数字

所以操作次数就是总数减去数量最多得数

B. Generate Permutation

题意

构造一个序列 \(p\) ,内部元素是 \([1,2,\cdots,n]\) 打乱

使长度为 \(n\) 的初始值为 \(-1\) 的序列经过操作后变成 \(p\)

要求用两台打字机时的 \(carriage\; return\) 操作的次数相同

思路

思路就是先写几个序列出来

比如 \(1,2,3\)\(3,1,2\)\(2,1,3\) ,每个序列从后往前和从前往后的return次数相加是4,是 \(n+1\) ,所以就是偶数不行

至于奇数怎么构造,还是用\(1,2,3\) ,从后往前是return3次

那就先搞个 \([1,2,\cdots,n]\) 再把前 \(\frac{n+1}{2}\) 个逆序一下,比如4321567

从后往前和从前往后都是 \(\frac{n+1}{2}\)

C. Guess The Tree

题意

这是一个[[交互型问题]],通过询问至多 \(15n\) 次,来获取一个 \(n\) 个结点的树

思路

我们可以根据返回值判断两个结点是不是直接相连,比如 \(x,y\)

如果是就存储起来

如果不是,设返回值是 \(z\) ,我们再问询 \(x,z\)\(y,z\)

我认为本题难度主要在于怎么组织代码

我是用一个 \(set\) 存储直接相连的点,用 \(map\) 存储所有询问过的结点

用一个队列存储上述的 \(x,z\)\(y,z\)

代码

#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

题意

找最长的不存在重复元素的子序列并输出

要求有多个的话,按照奇数位乘 \(-1\) 的字典序输出

就是奇数位尽量大,偶数位尽量小

思路

获取子序列的长度很简单,用一个 \(map\)\(map\)\(size\) 就是

假定子序列长度为 \(n\),先从简单的考虑,不考虑字典序的情况下,要判断一个位置能不能选中应该看当前位置及后面有多少可供选择的数

以样例 \([5,2,1,7,9,7,2,5,5,2]\) 为例,

第一次选择只能在前三个之间选,因为假定选了第四个数字 \(7\) ,后面不再有 \(1\) ,子序列最长只能到4

如果我们把 \(1\) 选了,那接下来我们可以在 \([5,2,\;,7,9]\) 之间选,但是又只能选1后面的数,所以就是79选其一

对于可供选择的区间,区间的左边界由于子序列的原因只能是不减的

区间的右边界由于子序列长度的原因,也是不减的

那么我们只要根据当前是奇数位还是偶数位,在可供选择的区间里选择最大或是最小值就行

就是双指针的思路,用[[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;
}
posted @ 2024-08-23 15:54  lulaalu  阅读(89)  评论(0编辑  收藏  举报