Practice.

A. Destroyer

题意

定义每个机器人具有一个参数:它的前面有多少机器人。

机器人可以排成很多行,因而我们可以得到最后的参数序列。

现在,给定打乱后的参数序列,输出是否合法。

思路

很显然,我们只需统计每个参数的出现次数,按照参数升序排序后,如果出现次数不递减,那么就是合法的,否然不合法。

时间复杂度:\(O(n \log n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n;
    cin >> n;
    vector<int> a(110);
    int mx = 0;
    for(int i=0;i<n;i++){
        int cur;
        cin >> cur;
        mx = max(mx, cur);
        a[cur] ++;
    }
    int pre = a[0];
    bool f = true;
    for(int i=1;i<=mx;i++){
        if(a[i] > pre){
            f = false;
            break;
        }
        pre = a[i];
    }
    cout << (f ? "YES\n" : "NO\n");
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

就是题目抽象了点

B. Astrophysicists

题意

给定 \(k\) 个金币,每个金币等价于 \(g\) 个银币。给定 \(n\) 个人,规定需要将所有银币分发给所有人。

对于任意一个人所分发的银币数量 \(x\),我们定义 \(r = x\mod g\),按照下面的规则得到盈利/亏损:

  1. 如果 \(r \geq \lceil \frac{g}{2} \rceil\),那么亏损 \(g - r\)

  2. 否则,盈利 \(r\)

输出最后能盈利最多多少钱,如果亏损,输出 \(0\)

思路

我们不妨不考虑分配的总额,那么最后要让盈利尽可能多,每个人分发 \(\lfloor \frac{g-1}{2} \rfloor\) 即可。

那么,考虑上总额,如果总额不够,依然是盈利的,我们将答案和总额取最小值即可。

但如果总额过多,我们记多出 \(p\) 银币。

因为存在取模操作,因而我们可以把多出的银币全都给一个人。

此时,我们将会失去一个人的盈利,并亏损一部分银币,我们进行分讨即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n, k, g;
    cin >> n >> k >> g;
    int ans = min((g - 1) / 2 * n, k * g), left = (k * g - ans) % g;
    if(left > 0){
        ans -= (g - 1) / 2;
        left = (left + (g - 1) / 2) % g;
        if(left < ceil((double) g / 2)) ans += left;
        else ans -= g - left;
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

抽象的,通俗易懂的,巧妙的

C. k-th equality

题意

对于二元加法 \(A + B = C\),给定 \(A, B, C\) 的位数以及一个整数 \(k\),输出按字典序排序的满足位数的第 \(k\) 个式子。

思路

首先,第一反应绝对是推式子,但,这题的 \(A,B,C\) 范围小到...可以暴搜。

我们对 \(A\) 爆搜,并统计对于当前 \(A\) 的选择,有多少个对应的式子,以此遍历得到答案。

显然,令 \(x = \max(A, B)\),我们只有两种情况:\(x = C, x + 1 = C\),前后者的区别是是否进位,我们分讨即可。

那么,除了这两种情况,其他情况均为无解。

时间复杂度:\(O(n ^ 2)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

const vector<int> p = {1, 10, 100, 1000, 10000, 100000, 1000000};

void solve() {
    int a, b, c, k;
    cin >> a >> b >> c >> k;
    bool ok = false;
    for(int i = p[a - 1]; i < p[a]; i ++){ //对a爆搜
        int l = max(p[b - 1], p[c - 1] - i),
            r = min(p[b] - 1, p[c] - i - 1);
        if(l > r) continue;
        if(r - l + 1 < k) k -= (r - l + 1);
        else{
            cout << i << " + " << l + k - 1 << " = " << i + l + k - 1 << '\n';
            ok = true;
            break;
        }
    }
    if(!ok) cout << -1 << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

不能爆搜的话得对四种情况分讨,挺麻烦,但好像能做