Educational Codeforces Round 76 (Rated for Div. 2) A - Two Rival Students B - Magic Stick C - Dominated Subarray D - Yet Another Monster Killing Problem E - The Contest

比赛链接:https://codeforces.com/contest/1257

题意

有 $n$ 个学生站成一排,最多使相邻两个学生交换位置 $k$ 次,给出两个学生的位置,计算这两个学生能相距的最远距离。

代码

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n, x, a, b; cin >> n >> x >> a >> b;
    cout << min(n - 1, abs(b - a) + x) << "
";
}

int main() {
    int t; cin >> t;
    while (t--) solve();
}

B - Magic Stick

题意

给出正整数 $x,y$,对 $x$ 可以选择执行两种操作:

  • 如果 $x$ 为偶数,将 $x$ 变为 $frac{3x}{2}$
  • 如果 $x>1$,将 $x$ 变为 $x - 1$

判断 $x$ 能否变为 $y$ 。 

代码

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int x, y; cin >> x >> y;
    if (x >= y or (x == 2 and y == 3)) cout << "YES" << "
";
    else cout << (x <= 3 ? "NO" : "YES") << "
";
}

int main() {
    int t; cin >> t;
    while (t--) solve();
}

C - Dominated Subarray

题意

找出一个数组大小至少为 $2$ 的最小连续子数组,要求该子数组中出现次数最多的值只有一个。

题解

依次计算每个值相邻两个元素的最短距离即可。

证明

如果一个值的相邻两个元素之间有出现两次的其他值,说明其他值相邻两个元素的距离更短,最终也一定会被取到。

代码

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n; cin >> n;
    vector<int> pos[n + 1];
    for (int i = 0; i < n; i++) {
        int x; cin >> x;
        pos[x].push_back(i);
    }
    int ans = INT_MAX;
    for (auto v : pos) {
        for (int i = 1; i < v.size(); i++) {
            ans = min(ans, v[i] - v[i - 1] + 1);
        }
    }
    cout << (ans == INT_MAX ? -1 : ans) << "
";
}

int main() {
    int t; cin >> t;
    while (t--) solve();
}

D - Yet Another Monster Killing Problem

题意

有 $n$ 个怪物在山洞中排成一排,每个怪物的力量为 $a_i$,你有 $m$ 个战士,每个战士的力量为 $p_i$,只有当战士的力量不低于怪物时才可以击败怪物,每个战士一天最多击败 $s_i$ 只怪物。一天只能选派一个战士,计算击败全部怪物所需的最少天数。(怪物被击败后不会复活)

题解

预处理一遍得到耐久度至少为 $i$ 的战士的最大力量 $mx_i$,之后利用双指针模拟,每天尽可能多地击败怪物即可。

代码

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n; cin >> n;
    int a[n] = {};
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    int m; cin >> m;
    int mx[n + 1] = {};
    for (int i = 0; i < m; i++) {
        int p, m; cin >> p >> m;
        mx[m] = max(mx[m], p);
    }
    for (int i = n - 1; i >= 0; i--) {
        mx[i] = max(mx[i], mx[i + 1]);
    }
    int ans = 0;
    for (int i = 0; i < n; ) {
        int j = i, maxn = a[i];
        while (j < n and mx[j - i + 1] >= maxn) {
            ++j;
            maxn = max(maxn, a[j]);
        }
        if (j == i) {
            ans = -1;
            break;
        } else {
            ++ans;
            i = j;
        }
    }
    cout << ans << "
";
}

int main() {
    int t; cin >> t;
    while (t--) solve();
}

E - The Contest

题意

三个人各持有一个排列的子序列,要求使第一个人持有排列的前一部分,第二个人持有排列的中间一部分,第三个人持有排列的后一部分,三人间至少要移动多少个元素。(可以移动一个人的全部元素)

题解

参考自:qscqesze

逆向考虑,将每个人持有的元素排序后合并为一个序列,最多不需移动的元素个数即为该序列的最长上升子序列。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
int k1, k2, k3, n;
int a[N], b[N], dp[N];

int lis() {
    int len = 1;
    b[1] = a[0];
    for (int i = 1; i < n; i++) {
        b[len + 1] = n + 1;
        int l = 0, r = len + 1, ans = 0;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (a[i] < b[mid]) {
                ans = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        b[ans] = a[i];
        if (ans > len) ++len;
    }
    return n - len;
}

int main() {
    cin >> k1 >> k2 >> k3;
    n = k1 + k2 + k3;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(a, a + k1);
    sort(a + k1, a + k1 + k2);
    sort(a + k1 + k2, a + k1 + k2 + k3);
    cout << lis() << "
";    
}