jiejiejiang2004

题解:CF contest 2037 : [Codeforces Round 988 (Div. 3)] (A-E)

A Twice

题面

Kinich 醒来迎接新的一天的开始。他打开手机,查看邮箱,发现了一份神秘的礼物。他决定打开礼物的盒子。

Kinich 用 \(n\) 整数打开数组 \(a\) 。最初,Kinich的分数是 \(0\) 。他将执行任意次数的以下操作:

—选择两个索引 \(i\)\(j\) \((1 \leq i \lt; j \leq n)\) ,确保在之前的操作中没有选择 \(i\)\(j\)\(a_i = a_j\) 。然后,将 \(1\) 加到他的分数上。

输出 Kinich 在执行上述操作任意次数后可以获得的最大分数。

题解

实际上我们不用管这个数组,我们只要把对出现次数大于 \(2\) 的数字进行记录处理就可以了

对每种出现次数大于 \(2\) 的数字,我们能操作 \(\lfloor 出现次数/2 \rfloor\)

最后对其求和即可

具体实现可以用 map ,因为范围不大,用数组存也可以。

代码

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define lowbit(x) x&-x

using i64 = long long;
using pii = std::pair<int,int>;

void solve() {
    int n;
    std::cin >> n;
    int ans = 0;
    std::map<int,int> mp;
    for(int i = 0 ; i < n ; i ++) {
        int num;
        std::cin >> num;
        mp[num] ++;
    }

    for(std::map<int,int>::iterator i = mp.begin() ; i != mp.end() ; i ++) {
        ans += i->second / 2;
    }

    std::cout << ans << "\n";
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    int t = 1;
    std::cin >> t;
    while(t--) {
        solve();
    }
    return 0;
}

B Intercepted Inputs

题面

为了帮助您为即将到来的 Codeforces 竞赛做准备,Citlali设置了一个网格问题,并试图通过您的输入流为您提供一个 \(n \times m\) 的网格。具体来说,你的输入流应该包含以下内容:

  • 第一行包含两个整数 \(n\)\(m\) —— 网格的尺寸。

  • 下面的 \(n\) 行包含 \(m\) 个整数 —— 网格的值。

然而,有人截获了你的输入流,打乱了所有给定的整数,并把它们都放在一行上!现在, \(k\) 整数都在一行中,并且您不知道每个整数最初属于哪里。您决定自己确定 \(n\)\(m\) 的值,而不是要求Citlali重新发送输入。

输出Citlali可能提供的 \(n\)\(m\) 的任何可能值。

题解

第一个数是 \(k\) 代表下列一行的长度

下面一行是数,包括矩阵的数字 以及 矩阵的长和宽

因此,矩阵的大小已经确定,即 \(n \times m = k - 2\)

也就是说,我们只要在下面一行中找到能够构成 相乘的乘积等于 \(k-2\) 的一对数即可(不管顺序)

  1. 最容易想到的暴力算法,时间复杂度为 \(O(n^2)\) ,超时
  2. 我们可以在 \(O(n)\) 的时间复杂度内实现
    从前往后遍历,如果前面出现过能与当前数相乘得到 \(k-2\) 的数,就可以直接输出
    具体存储结构可以用 map(不知道可不可以用数组)

代码

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define lowbit(x) x&-x

using i64 = long long;
using pii = std::pair<int,int>;

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for(int i = 0 ; i < n ; i ++) {
        std::cin >> a[i];
    }

    std::map<int,int> mp;

    int m = n - 2;
    for(int i = 0 ; i < n ; i ++) {
        if(m%a[i] == 0) {
            if(mp.count(m/a[i])) {
                std::cout << a[i] << " " << m / a[i] << "\n";
                return;
            }
        }
        mp[a[i]] ++;
    }
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    int t = 1;
    std::cin >> t;
    while(t--) {
        solve();
    }
    return 0;
}

C Superultra's Favorite Permutation

题面

超级,一只小熊猫,拼命想要原始宝石。在他的梦中,一个声音告诉他,他必须解决以下任务,以获得一生的原始宝石供应。帮助Superultra !

构造一个长度为 \(n\) 的排列 \(^{\text{∗}}\) \(p\) ,使得 \(p_i + p_{i+1}\) 是所有 \(1 \leq i \leq n - 1\) 的组合 \(^{\text{†}}\) 。如果不可能,输出 \(-1\)

\(^{\text{∗}}\) 长度为 \(n\) 的排列是一个由 \(1\)\(n\) 之间任意顺序的 \(n\) 不同整数组成的数组。例如, \([2,3,1,5,4]\) 是一个排列,但 \([1,2,2]\) 不是一个排列( \(2\) 在数组中出现了两次), \([1,3,4]\) 也不是一个排列( \(n=3\) 但数组中有 \(4\) )。

\(^{\text{†}}\) 如果一个整数 \(x\) 除了 \(1\)\(x\) 之外至少有一个除数,那么这个整数 \(x\) 就是合数。例如, \(4\) 是合数,因为 \(2\) 是一个除数。

题解

已知:非 \(2\) 且 非 \(0\) 的 偶数一定是合数

已知:奇数和奇数相加是偶数(大于 \(2\) ),偶数和偶数相加是偶数(大于 \(2\) ),奇数和偶数相加是奇数

已知:最小的奇数合数是 \(9\) ,可以由 \(4\)\(5\) 相加得到

因此:

  • \(n \lt 5\) 时,不可能构造成功,直接输出 \(-1\);

  • \(n \ge 5\) 时,把 \(5\)\(4\) 放中间。其余奇数和偶数分别放两边,即构造成功

代码

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define lowbit(x) x&-x

using i64 = long long;
using pii = std::pair<int,int>;

void solve() {
    int n;
    std::cin >> n;
    if(n < 5) {
        std::cout << -1 << "\n";
        return;
    }

    for(int i = 1 ; i <= n ; i += 2) {
        if(i == 5) continue;
        std::cout << i << " ";
    }

    std::cout << 5 << " " << 4 << " ";

    for(int i = 2 ; i <= n ; i += 2) {
        if(i == 4) continue;
        std::cout << i << " ";
    }
    std::cout << "\n";
 }

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    int t = 1;
    std::cin >> t;
    while(t--) {
        solve();
    }
    return 0;
}

D Sharky Surfing

题面

Mualani 喜欢在她的鲨鱼冲浪板上冲浪!

Mualani 的冲浪路径可以用数轴来表示。她从位置 \(1\) 开始,路径在位置 \(L\) 结束。当她在位置 \(x\) ,跳跃能力为 \(k\) 时,她可以跳到区间 \([x, x+k]\) 内的任何位置。最初,她的跳跃功率为 \(1\)

然而,她的冲浪之路并非一帆风顺。在她的道路上有许多障碍。每个跨栏用区间 \([l, r]\) 表示,这意味着她不能跳到区间 \([l, r]\) 中的任何位置。

在路径上的某些位置也有能量提升。上电 \(i\) 位于 \(x_i\) 位置,值为 \(v_i\) 。当 Mualani 在位置 \(x_i\) 时,她可以选择收集能量以增加她的跳跃能力 \(v_i\) 。在同一个位置可能会有多个升级道具。当玩家处于拥有一些能量道具的位置时,他可能会选择接受或忽略每个能量道具。在任何障碍的间隔中都没有能量。

到达位置 \(L\) 完成路径,她必须收集的能量的最小数量是多少?如果无法完成冲浪路径,则输出 \(-1\)

题解

这题是一道典型的贪心,他甚至给你排好序了!感动死了。

我们只需要一个优先队列去维护就可以了:

在一个障碍之前,把前面的道具 push 进队列,然后如果你的跳跃能力(以下简称 jump )不足以跳过时,就从队列里拿出来一个道具,加到 jump 里面

如果即使队列空了,这个 jump 还是不足以跳过去,那就直接输出 \(-1\) 结束

如果全部都 ok 了,那就输出加了多少个道具。

(感觉还是有点不是很清晰,不懂的可以在评论区问)

代码

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define lowbit(x) x&-x

using i64 = long long;
using pii = std::pair<int,int>;

void solve() {
    int n,m,l;
    std::cin >> n >> m >> l;
    std::vector<pii> str(n),power(m);

    for(int i = 0 ; i < n ; i ++) {
        std::cin >> str[i].first >> str[i].second;
    }
    for(int i = 0 ; i < m ; i ++) {
        std::cin >> power[i].first >> power[i].second;
    }

    int ans = 0;
    int jump = 1;
    int idx = 0;

    std::priority_queue<int> q;
    for(int i = 0 ; i < n ; i ++) {
        while(idx < m && power[idx].first < str[i].first) {
            q.push(power[idx].second);
            idx ++;
        }

        while(str[i].first - 1 + jump <= str[i].second && !q.empty()) {
            jump += q.top();
            q.pop();
            ans ++;
        }

        if(str[i].first - 1 + jump <= str[i].second) {
            std::cout << -1 << "\n";
            return;
        }
    }

    std::cout << ans << "\n";
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    int t = 1;
    std::cin >> t;
    while(t--) {
        solve();
    }
    return 0;
}

E Kachina's Favorite Binary String (交互题)

题面

这是一个互动的问题。

Kachina挑战你猜她最喜欢的长度为 \(n\) 的二进制字符串 \(^{\text{∗}}\) \(s\) 。她将 \(f(l, r)\) 定义为 \(s_l s_{l+1} \ldots s_r\)\(\texttt{01}\) 的子序列 \(^{\text{†}}\) 的个数。**如果两个子序列是通过删除原字符串中不同位置的字符而形成的,则认为它们是不同的,即使结果子序列由相同的字符组成

要确定 \(s\) ,你可以问她一些问题。在每个问题中,您可以选择两个索引 \(l\)\(r\) ( \(1 \leq l \lt r \leq n\) ),并询问她 \(f(l, r)\) 的值。

在向克钦纳询问不超过 \(n\) 的问题后,确定并输出 \(s\) 。然而, \(s\) 可能是无法确定的。在这种情况下,您需要报告 \(\texttt{IMPOSSIBLE}\)

形式上, \(s\) 是不可能确定的,如果在问了 \(n\) 个问题之后,无论问什么问题, \(s\) 总是有多个可能的字符串。请注意,如果您报告\(\texttt{IMPOSSIBLE}\)**,当存在最多 \(n\) 查询序列将唯一地确定二进制字符串时,您将得到错误的答案判决

\(^{\text{∗}}\) 二进制字符串只包含字符 \(\texttt{0}\)\(\texttt{1}\)

\(^{\text{†}}\) 序列 \(a\) 是序列 \(b\) 的子序列,如果 \(a\) 可以从 \(b\) 中删除几个(可能为零或全部)元素。例如, \(\mathtt{1011101}\) 的子序列是 \(\mathtt{0}\)\(\mathtt{1}\) , \(\mathtt{11111}\) , \(\mathtt{0111}\) ,但不是 \(\mathtt{000}\)\(\mathtt{11100}\)

题解

这是一道 交互题

首先!我们确定一下, \(\texttt{IMPOSSIBLE}\) 的情况暂时确定为 \(f(1,n) = 0\) 的情况,因为可能全是 \(1\) 或者全是 \(0\) (其实也就这种情况)

其次!如果 \(f(1,i-1) \lt f(1,i)\) ,那么第 \(i\) 位一定是 \(1\) ,因为能和前面的 \(0\) 组成 \(01\)

同时!如果 \(f(1,i-1) = f(1,i)\) ,那么第 \(i\) 位一定是 \(0\) ,因为不能和前面的数字组成 \(01\)

然后!我们设 \(\texttt{last}\) 为最前面的且 \(f(1,\texttt{last}) \gt 0\)\(1\) 的位置,那么 $f(1,\texttt{last}) \lt \texttt{last} $ ,同时,\(\texttt{last}\) 前面应该按从前往后顺序为 \(\texttt{last} - 1 - f(1,\texttt{last})\)\(1\)\(f(1,\texttt{last})\)\(0\)

(另外!) 如何找到 \(\texttt{last}\) ? 只要判定 $f(1,\texttt{last}) \lt \texttt{last} $ 即可。

最后!我们可以发现按照上面的方法,从后往前遍历确认的话(记得用数组存起来哦),我们无法确定最前面两位的数字是什么。刚好我们还有最后一次询问机会,那我们直接得到 \(f(2,n)\)

  • 如果 \(f(2,n) = f(1,n)\) 说明最前面的位一定是 \(1\) ,至于第二位是什么,就比较 \(2\)\(f(1,last)\) 的关系,如果 \(\texttt{last} - 2 \le f(1,last)\) 那么这一位上就是 \(0\) ,否则就是 \(1\)
  • 如果 \(f(2,n) \neq f(1,n)\) 说明最前面一位一定是 \(0\) ,至于第二位是什么,就直接看 \(f(1,2)\) ,如果 \(f(1,2) \gt 0\) 那么第二位就是 \(1\) ,否则就是 \(0\)

但是但是!如果这个串只有两位的话,那么最后一步就会报错(OJ,操作不合法,? 2 2 是不合法的),我们只需要在最前面特判一下 \(n = 2\) 时的情况即可(只有序列为 \(01\) 可以确认,其他直接输出 \(\texttt{IMPOSSIBLE}\)

代码

#include <bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define lowbit(x) x&-x


const int INF = 1e18;

using i64 = long long;
using pii = std::pair<int,int>;

void solve() {
    int n;
    std::cin >> n;
    std::string s;

    int rans = INF;
    int ans;
    int last = INF;

    if(n == 2) {
        std::cout << "? " << 1 << " " << n << std::endl;
        std::cin >> rans;

        if(rans == 0) {
            std::cout << "! IMPOSSIBLE" << std::endl;
        } else {
            std::cout << "! 01" << std::endl;
        }

        return;
    }

    std::vector<int> mem(n+1);

    std::cout << "? " << 1 << " " << n << std::endl;
    std::cin >> rans;
    ans = rans;

    int sum = rans;

    if(sum == 0) {
        std::cout << "! IMPOSSIBLE" << std::endl;
        return;
    }

    mem[n] = rans;
    
    for(int i = n-1 ; i > 1 ; i--) {

        std::cout << "? " << 1 << " " << i << std::endl;
        std::cin >> ans;

        if(ans == 0 && rans != 0) {
            last = i+1;
        }

        if(ans == rans){
            if(ans == 0) {
                if(last - (i+1) <= mem[last]){
                    s.push_back('0');
                } else {
                    s.push_back('1');
                }
            } else {
                s.push_back('0');
            }
        } else {
            s.push_back('1');
        }

        mem[i] = ans;

        rans = ans;
    }

    if(last == INF) {
        last = 2;
    }

    std::cout << "? " << 2 << " " << n << std::endl;
    std::cin >> ans;

    if(ans == sum) {
        if(last - 2 <= mem[last]) {
            s.push_back('0');
        } else {
            s.push_back('1');
        }
        s.push_back('1');
    } else {
        if(mem[2]) {
            s.push_back('1');
        } else {
            s.push_back('0');
        }
        s.push_back('0');
    }

    std::reverse(all(s));

    std::cout << "! " << s << "\n";
}

signed main() {

    int t = 1;
    std::cin >> t;
    while(t--) {
        solve();
    }
    return 0;
}

posted on 2024-11-18 11:18  Jiejiejiang  阅读(31)  评论(0编辑  收藏  举报

导航