Contestant. Rank 3058. Rating -35.

坐大牢局

A. Showstopper

题意

给定两个序列 \(a, b\),规定一次操作为任取 \(i\),交换 \(a_i, b_i\)。输出任意次操作后,是否可以让 \(a_n = \max(a_i), b_n = \max(b_i)\)

思路

首先,既然最后方案我们无需考虑,那么我们不妨定义 \(a\) 为最小值的序列,\(b\) 为最大值的序列,那么只要满足上面的条件,就是有解,否则无解。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for(int i=0;i<n;i++) cin >> a[i];
    for(int i=0;i<n;i++) cin >> b[i];
    if(a[n - 1] > b[n - 1]) swap(a[n - 1], b[n - 1]);
    bool f = true;
    for(int i=0;i<n-1;i++) {
        int mn = min(a[i], b[i]), mx = max(a[i], b[i]);
        if (mn > a[n - 1] || mx > b[n - 1]) f = false;
    }
    cout << (f ? "Yes\n" : "No\n");
}

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

猜结论题 x1

B. Three Sevens

题意

给定 \(n\) 局的参赛情况,在前面一局胜利的玩家不会参与下面的比赛。输出任意一种获胜玩家列表排列。

思路

我们不妨反过来考虑,也就是说,在后面出现的玩家一定不是前面的赢家,那么我们直接倒着遍历即可。

首先,我们遍历参加了该局的玩家,若只要有一个没被标记,那么就是有解的,我们随便输出一个即可。

然后,我们标记参加了该局的玩家,这样即可防止其在前面作为赢家。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    vector<vector<int>> a(n, vector<int>());
    for(int i=0;i<n;i++){
        int cnt;
        cin >> cnt;
        a[i] = vector<int>(cnt);
        for(int j=0;j<cnt;j++) cin >> a[i][j];
    }
    vector<bool> st(50010, false);
    vector<int> ans(n);
    for(int i=n-1;i>=0;i--){
        bool f = false;
        for(auto e : a[i]){
            if(!st[e]) ans[i] = e, f = true;
            st[e] = true;
        }
        if(!f){
            cout << -1 << '\n';
            return;
        }
    }
    for(auto e : ans) cout << e << ' ';
    cout << '\n';
}

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

这可比A题好做多了

C. Candy Store

题意

给定 \(n\)\(a_i, b_i\),定义 \(d_i\)\(a_i\) 的因数,\(c_i = b_id_i\)

构造一组 \(d\),将 \(c\) 划分为 \(x\) 段值相等的序列,输出 \(x_{\min}\)

思路

我们来考虑 \(x = 1\) 的情况:

首先,\(c_i = b_id_i\),也就是说,\(c_i\)\(b_i\) 的倍数。那么,\(c_1 \times c_2 \times \ldots \times c_n\)\(lcm(b_1, b_2, \ldots, b_n)\) 的倍数。

其次,\(d_i\)\(a_i\) 的因数,那么 \(b_id_i\) 就是 \(a_ib_i\) 的因数,也就是说,\(gcd(a_1b_1, a_2b_2, \ldots, a_nb_n)\)\(c_1 \times c_2 \times \ldots \times c_n\) 的倍数。

所以,\(gcd(a_1b_1, a_2b_2, \ldots, a_nb_n) \% lcm(b_1, b_2, \ldots, b_n)=0\) 就是其能成为一段区间的条件。

显然,如果一个元素能划分到前面的序列中,那么我们完全可以不考虑它,因为就算把他放进来,不影响数量。

因此,我们可以贪心地直接遍历统计个数。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

int gcd(int a, int b){
    return b == 0 ? a : gcd(b, a % b);
}

int lcm(int a, int b){
    return a * b / gcd(a, b);
}

void solve() {
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for(int i=0;i<n;i++){
        cin >> a[i] >> b[i];
    }
    int g = 0, l = 1, ans = 1;
    for(int i=0;i<n;i++){
        g = gcd(g, a[i] * b[i]);
        l = lcm(l, b[i]);
        if(g % l != 0){
            ans ++;
            g = a[i] * b[i];
            l = b[i];
        }
    }
    cout << ans << '\n';
}

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

没想到啊没想到...

D. Shocking Arrangement

题意

给定一个总和为 \(0\) 的序列,重新排序这个序列,满足

\(\max\limits_{1 \le l \le r \le n} \lvert a_l + a_{l+1} + \ldots + a_r \rvert < \max(a_1, a_2, \ldots, a_n) - \min(a_1, a_2, \ldots, a_n),\)

若不能满足,输出 \(No\)

思路

首先,我们定义 \(sum_i\) 为前 \(i\) 个数的前缀和,那么

\(\max\limits_{1 \le l \le r \le n} \lvert a_l + a_{l+1} + \ldots + a_r \rvert = sum_{\max} - sum_{\min}\)

那么,我们让 \(sum_{\max} < \max(a_1, a_2, \ldots, a_n), sum_{\min} > \min(a_1, a_2, \ldots, a_n)\) 即可。

如何实现?我们不妨随便放一个数上去,然后记录当前放入的前 \(i\) 个数的和 \(x\),若 \(x > 0\),那么我们加上一个负数,直到 \(x \leq 0\),反之亦然。

考虑到总和为 \(0\),所以如果 \(x > 0\),剩余的数的和一定为 \(-x\),所以后面一定会有几个负数,让 \(x \leq 0\)

此时,一定有解,而若要判无解,当且仅当整个序列都是 \(0\)

对细节的证明

思路的第一句话是贪心的(有乱猜的嫌疑)。

为什么呢?因为,\(sum_{\max}\) 不一定在 \(sum_{\min}\) 右边。

或者说,\(S_x, S_y, S_z, Sx>0, S_y < 0, S_z > 0\),这样的区间要如何保证 \(|S_y| < \max(a_1, a_2, \ldots, a_n) - \min(a_1, a_2, \ldots, a_n)\) 呢?

首先,根据思路,\(S_x\) 一定小于等于 \(\max(a_1, a_2, \ldots, a_n)\),而 \(|S_x| \geq |S_{y-1}|\),所以

\(|S_y| < \max(a_1, a_2, \ldots, a_n) + abs(\min(a_1, a_2, \ldots, a_n))\)

因此得证。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    stack<int> p, q;
    for(int i=0;i<n;i++){
        int cur;
        cin >> cur;
        if(cur >= 0) p.emplace(cur);
        else if(cur < 0) q.emplace(cur);
    }
    if(q.empty()){
        cout << "No\n";
        return;
    }
    cout << "Yes\n";
    int now = 0;
    for(int i=0;i<n;i++){
        if(now <= 0){
            now += p.top();
            cout << p.top() << ' ';
            p.pop();
        }else{
            now += q.top();
            cout << q.top() << ' ';
            q.pop();
        }
    }
    cout << '\n';
}

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

猜结论题 x2