Contestant~. Rank 1938. Rating -45.

掉分日记.md

A. United We Stand

题意

给定一个数组 \(a\),将其分成两个非空数组 \(b, c\),满足任意一个 \(c\) 中的元素都不是任意一个 \(b\) 中的元素的约数。

思路

很显然,若 \(x > y\),则 \(x\) 一定不可能是 \(y\) 的约数。

因而,找出数组中最大值,并将等于最大值的所有数都放到 \(c\) 中,剩余的放到 \(b\) 中即可。

显然,若 \(a\) 中所有元素都相同,那么无解,否则必有解。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

//#define FLOATING_OCEAN

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

const int N = 1e5 + 10, M = 2e5 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i=0;i<n;i++) cin >> a[i];
    sort(rall(a));
    int cnt = 0;
    for(int i=0;i<n;i++){
        if(a[i] == a[0]) cnt ++;
        else break;
    }
    if(cnt == n){
        cout << -1 << '\n';
        return;
    }
    cout << n - cnt << ' ' << cnt << '\n';
    for(int i=cnt;i<n;i++) cout << a[i] << ' ';
    cout << '\n';
    for(int i=0;i<cnt;i++) cout << a[i] << ' ';
    cout << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

速签

B. Olya and Game with Arrays

题意

给定 \(n\) 个序列,定义操作如下:

  1. 遍历所有序列;

  2. 对于每一个序列,取出一个数,放入任意一个其他序列中

更具体地说,每一个序列最多取出一个元素,但可以放入任意数量的元素。

输出操作之后,所有序列最小值之和的最大值。

思路

我们既然希望最小值之和尽可能小,那么我们肯定希望把每个序列的最小值都尽可能拿走。

显然,所有序列中的最小值无法不参与计算,且所有的最小值都可以转移到同一个序列中。

我们记所有最小值都转移到了序列 \(p\) 中。

那么,最后我们的答案就是 所有序列的次小值之和 \(-\) \(p\) 的次小值 \(+\) 所有序列的最小值。

那么,很显然,最后答案就是 次小值之和 \(-\) 次小值的最大值 \(+\) 最小值

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

//#define FLOATING_OCEAN

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

const int N = 1e5 + 10, M = 2e5 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<vector<int>> a(n + 1);
    int mn = inf, sum = 0, mn2 = inf;
    for(int i=1;i<=n;i++){
        int c;
        cin >> c;
        a[i] = vector<int>(c);
        for(int j=0;j<c;j++) cin >> a[i][j];
        sort(all(a[i]));
        sum += a[i][1];
        mn = min(mn, a[i][1]);
        mn2 = min(mn2, a[i][0]);
    }
    cout << sum - mn + mn2 << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

为什么做了这么久呢?为什么呢?

C. Another Permutation Problem

题意

给定整数 \(n\),构造一个 \(n\) 的排列 \(p\),使 \(\displaystyle (\sum_{i = 1}^{n} p_i \cdot i) - (\max_{j = 1}^{n} p_j \cdot j)\) 最大。

思路

这道题很有趣,\(std\) 的复杂度是 \(O(n ^ 3)\),但存在一种 \(O(n ^ 2)\) 的做法。

做法很好猜,我们直接枚举拐点,从拐点开始,从 \(n\) 开始倒着放,变成诸如 \(1, 2, 3, 6, 5, 4\) 的形式,然后代入即可。

为什么呢?我也不知道。

引用官方题解的一句话:

We know about the \(O(N^2)\) solution in C, but we did not find a good suitable proof for it (and, using the method, we could achieve faster solutions).

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

//#define FLOATING_OCEAN

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

const int N = 1e5 + 10, M = 2e5 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    int sum = 0;
    for(int k=1; k <= n; k++) {
        int t = 0, mx = 0;
        for (int i = 1; i < k; i++) {
            t += i * i;
            mx = max(i * i, mx);
        }
        int now = n;
        for (int i = k; i <= n; i++) {
            t += now * i;
            mx = max(mx, now * i);
            now--;
        }
        t -= mx;
        sum = max(sum, t);
    }
    cout << sum << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

尝试找到一个证明,但失败了

D. Andrey and Escape from Capygrad

题意

给定 \(n\) 个传送装置,对应到数轴上,装置由两条线段表征。

两条线段的端点分别为给定的 \(l, r, a, b\),满足 \(l \leq a \leq b \leq r\)。若位于 \([l, r]\) 上,那么可以传送到 \([a, b]\)

现在,给定 \(q\) 个询问,每次询问给定一个点的坐标 \(x_j\),输出最远能到达的点的下标。

思路

首先,我们可以贪心地认为,如果我们要使用某一个传送装置 \(i\),一定会传送到 \(b_i\),而非其他点。

证明是很显然的,因为如果我们希望从 \(p\) 传送到 \(b_i\) 的右边,那么我们也一定可以从 \(b_i\) 传送。

那么,我们也可以得到另一个结论:\(r_i\) 毫无用处。

因此,我们考虑使用扫描线,离线处理答案并输出。

我们记 \(ans_i\)\(i\) 点可以到达的最大坐标,\(p_j\) 为第 \(j\) 个查询的答案,那么显然 \(p_j = \max(x_j, \max_{i = 1}^n {ans_i \vert l_i \le x_j \le r_i})\)

具体地说,我们考虑从后往前枚举 \(b_i, x_j, l_i\),并维护一个数据结构来更新 \(ans_i, p_j\)

  1. 对于 \(b_i\),我们将 \(ans_i\) 更新为数据结构中的最大值,并将最大值放入数据结构中;

  2. 对于 \(x_j\),我们将 \(p_j\) 更新为数据结构中的最大值;

  3. 对于 \(l_i\),我们将 \(ans_i\) 从数据结构中删去

如上,为了快速得到最大值,我们使用 \(\mathtt{multiset}\),对于删除重复元素的其中一个元素,我们考虑使用 \(\mathtt{extract}\) 方法。

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

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e5 + 10, M = 2e5 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<tpi> ask;
    vector<int> a(n);
    for(int i=0;i<n;i++){
        int l, r, A, b;
        cin >> l >> r >> A >> b;
        a[i] = b;
        ask.pb(b, 3, i);
        ask.pb(l, 1, i);
    }
    int q;
    cin >> q;
    //离线
    vector<int> ans(q);
    for(int i=0;i<q;i++){
        cin >> ans[i];
        ask.pb(ans[i], 2, i);
    }
    sort(rall(ask));
    multiset<int> st;
    for(auto [x, op, ind] : ask){
        if(op == 3){
            if(!st.empty()) a[ind] = max(a[ind], *st.rbegin());
            st.emplace(a[ind]);
        }else if(op == 2){
            if(!st.empty()) ans[ind] = max(ans[ind], *st.rbegin());
        }else st.extract(a[ind]); //extract保证只删除最早的一个
    }
    for(auto e : ans) cout << e << ' ';
    cout << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

没想到这个贪心,直接想合并区间去了(