Codeforces Global Round 10 [A - F]

Codeforces Global Round 10 [A - F]

A. Omkar and Password

题目大意

给定一个长度为\(n\)的序列\(a\),每次你能够从中选择相邻但不相等的两个元素\(a_i\),\(a_{i+1}\),将这两个数进行合而且替换为\(a_i + a_{i+1}\)。例如 \([7,4,3,7] \rightarrow [7,4+3,7]\),直到没法继续进行这样的操做,最后返回最终序列的长度。c++

1 <= n <= 2*10^5git

1 <= ai <= 10^9算法

考点: greedy math *800数组

解题思路

经过思考咱们能够发现,除非序列全为相同的数字,不然咱们必定能够将最后的序列长度压缩至\(1\)ide

所以能够写出以下代码:spa

代码

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

const int maxn = 2e5 + 50;
int f[maxn];

void solve(){
    int n; cin >> n;
    for (int i = 0; i < n; ++ i) cin >> f[i];

    sort(f, f + n);
    if (f[0] == f[n - 1]) cout << n << '\n';
    else cout << "1\n";
}

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

B. Omkar and Infinity Clock

题目大意

给定一个长度为\(n\)的序列\(a\),对其进行\(k\)次操做,每次操做为:code

  1. 取出当前序列的最大值\(d\)
  2. 对于序列中的每一个数\(a_i\),将其变为\(d - a_i\)

返回进行\(k\)次操做以后序列的状况。ip

1 <= n <= 2*10^5ci

1 <= k <=10^18get

考点: implementation math *800

解题思路

在进行第一次操做后,序列中的最大值和最小值已经固定,其中最大值为:\(\max{arr} - \min{arr}\),最小值为\(0\),以后再进行操做其实是一种周期性的重复。

(小tips:在看到\(k\)的数据范围时,能够猜想这题是一个周期性问题!)

代码

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

const int maxn = 2e5 + 50;
int f[maxn];
int ans[maxn][2];
int main(){
    int t; cin >> t;
    while (t--){
        int n;
        LL k;
        cin >> n >> k;

        int mn = 0x3f3f3f3f, mx = 0xc0c0c0c0;
        for (int i = 0; i < n; ++ i){
            cin >> f[i];
            mn = min(mn, f[i]);
            mx = max(mx, f[i]);
        }

        for (int i = 0; i < n; ++ i) ans[i][0] = mx - f[i];
        for (int i = 0; i < n; ++ i) ans[i][1] = (mx - mn) - ans[i][0];

        if (k & 1){
            for (int i = 0; i < n; ++ i) cout << ans[i][0] << (i == n - 1 ? '\n' : ' ');
        }else {
            for (int i = 0; i < n; ++ i) cout << ans[i][1] << (i == n - 1 ? '\n' : ' ');
        }
    }
    return 0;
}

C. Omkar and Waterslide

题目大意

给定一个长度为 \(n\) 的数组 \(a\) ,每次能够选取一个非降序列,使得序列中每一个值增长一。问最少操做多少次使得整个数组 非降

(\(1 \leq n \leq 2 * 10^5\))

(\(0 \leq a_i \leq 10^9\))

greedy implementation *1200

解题思路

分析后能够发现,只须要考虑谷底值。因为要求整个序列非降,所以咱们只须要考虑\(a_i\)\(a_{i - 1}\)的关系。(左值)

  • 当碰到 \(a_i \leq a_{i - 1}\) 时须要进行操做,将\(a_i \rightarrow a_{i + 1}\)
  • 当碰到 \(a_i \ge a_{i - 1}\) 时,则不须要考虑。

通俗的话来说,每次你只须要考虑可否知足当前值\(a_i\)大于等于前一个值\(a_{i - 1}\)

代码

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int maxn = 2e5 + 50;
int f[maxn];

void solve(){
    int n; cin >> n;
    for (int i = 0; i < n; ++ i) cin >> f[i];

    LL ans = 0;
    for (int i = 0; i + 1 < n; ++ i){
        ans += max(f[i] - f[i + 1], 0); // 只须要考虑左值
    }
    cout << ans << '\n';
}

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

D. Omkar and Bed Wars

题目大意

\(1\)\(n\)围成一个圈, 每一个人能够选择攻击左边(L)的人,或者右边(R)的人。且须要知足如何规则:

  • 当只被一我的攻击时,必须攻击这我的。
  • 当没有被攻击或者被两我的攻击时,能够攻击身边的任意一我的。

给定一个长度为 \(n\) 的序列表明攻击状况,问最少须要修改几回序列能使其知足规则。

(\(3 \leq n \leq 2*10^5\))

考点:constructive algorithms dp greedy string suffix structures *1700

解题思路

通过分析,能够发现只要存在LLL,RRR都是不合理的。要进行替换。也就是将其中某个位置的字符改变。

以后,须要考虑如何对于这种LLL...LLL序列进行处理能使获得的结果最优。

  • RRLRR改变一个字符,最多能处理长度为 5 的重复串
  • RRLRRLRR 改变两个字符,最多处理长度为 8 的重复串

而后咱们能够得出结论:对于长度为 \(n,n \ge 3\) 的重复串(注意,这里咱们没有考虑首尾链接的状况),咱们只须要改变 \(\frac{n}{3}\)

如今考虑首位链接的状况,当首尾链接时,长度为 \(n,n \ge 3\) 的重复串须要进行几回操做,因为首尾相连,所以首端和尾端能放置的字符从原来的 2 个,到如今的最多 1 个。也就是从RRLRR 改变为 RLR。所以咱们只须要改变 \(\frac{n + 2}{3}\) 次。(逆向思惟,认为咱们重复串的长度为 \(n + 2\))。

在书写代码的时候,能够首先将尾部与首部相同的字符去除,并修改计数器(至关于将其转移到首部)。以后判断是否整个序列为相同字符,若整个序列相同则特殊处理。

代码

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


void solve(){
    int n; cin >> n;
    string ss; cin >> ss;

    int cnt = 0, ans = 0;
    // 将尾部转移到首部(经过修改 cnt 完成)
    while (!ss.empty() && ss[0] == ss.back()){ 
        ++ cnt;
        ss.pop_back();
    }
    if (ss.empty()){
        if (cnt <= 2){
            cout << 0 << '\n';
            return;
        }
        if (cnt == 3){
            cout << 1 << '\n';
            return;
        }
        cout << (cnt + 2) / 3 << '\n';
        return;
    }

    ss += '$'; // 添加一个字符,保证能所有处理完成
    for (int i = 0; i + 1< ss.size(); ++ i){
        ++ cnt;
        if (ss[i] != ss[i + 1]){
            ans += (cnt / 3);
            cnt = 0;
        }
    }
    cout << ans << '\n';
}


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

后记

该题还有 dp 解法,后面补上

E. Omkar and Duck

题目大意

给定一个 \(n\) ,生成一个\(n \times n\)的矩阵。要求给定一个\(k\)值,输出惟一肯定的路径 \((1, 1) \rightarrow (n, n)\) 如此。

bitmasks constructive algorithms math *2100

解题思路

新知识点补充(这题还不够熟练):

  • 面对须要肯定惟一路径时,能够想到2的幂,也就是经过二进制 \(2^n\) ,来构造一个惟一肯定的序列

代码

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



int main(){
    int n; cin >> n;
    
    vector<vector<LL> > mat(n + 1, vector<LL>(n + 1 , 0));
    for (int i = 1; i <= n; ++ i){
        for (int j = 1; j <= n; ++ j){
            if (i & 1)  cout << "0 ";
            else cout << (1LL << (i + j)) << ' ';
        }
        cout << endl;
    }

    cout.flush();
    int q; cin >> q;
    while (q--){
        LL sum; cin >> sum; 
        cout << "1 1\n";
        int row = 1, col = 1;
        for (int k = 1; k <= 2 * n - 2; ++ k){
            int cur = col + row;
            if (row & 1){
                if (sum & (1LL << (cur + 1))) ++ row;
                else ++ col;
            }else{
                if (sum & (1LL << (cur + 1))) ++ col;
                else ++ row;
            }
            cout << row << " " << col << endl;
        }
    }
    cout.flush();

    return 0;
}

F. Omkar and Landslide

题目大意

给定一个长度为 \(n\) 的升序序列 \(H\) ,任意时刻序列中存在 \(h_i + 2 \leq h_{i + 1}\) 时,发生“滑坡”,即\(h_i\) 加一, \(h_{i +1}\) 减一。且全部滑坡同时进行。请问最后序列\(H\)的最终状态。

\(1 \leq n \leq 10^6\)

\(0 \leq hi \leq h_{i+1} \leq 10^{12} \quad \forall \;i \in [1, n]\)

constructive algorithms data structures greedy math *2400

解题思路

(这部分为特殊思路:看到 \(n\) 的规模以及最终时刻这两个字眼,我内心就想到了这题是一个数学贪心构造题。后面补题的时候发现果真是的)

F题的解题核心在于获得最终状态的条件。考虑到实际上只须要判断四个点就能够模拟滑坡的过程,咱们选取\(a_{i - 1}, a_{i}, a_{i + 1}, a_{i + 2}\)进行考虑。咱们假设\(a_{i},a{i - 1}\)之间;\(a_{i + 1}, a_{i + 2}\)之间不会进行滑坡,意味着\(a_{i + 2} - a_{i + 1} \leq 1\)(0, 1两种状态)。设当前\(a_{i + 1} - a{i} == 2\),进行滑坡以后就会出现\(a_{i} == a_{i + 1}\)。而因为\(a_{i + 1}\)减小了一,因此\(a_{i + 2} - a_{i + 1} \ge 1\)因此,每一个新的等式出现必定会破坏原有的一个等式

整个序列中最终只会出现最多一对相等的元素

咱们能够经过贪心构造出一个递增的数列,将剩下未分配的值贪心分配给前几个元素。

代码

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

inline LL read(){
    /* 注意假如为 long long 时须要修改 */
    char c = getchar();
    LL x = 0, f = 1;
    while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); }
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c^48), c = getchar();
    return f * x;
}

const int maxn = 1e6 + 50;
LL a[maxn];
int main(){
    LL n; n = read();
    LL sum = 0;
    for (int i = 0; i < n; ++ i) a[i] = read(), sum += a[i];

    sum -= (n * (n + 1)) / 2;
    LL eve, lvf;
    eve = sum / n;
    lvf = sum % n;
    for (int i = 0; i < n; ++ i){
        cout << (i + 1) + eve + (i + 1 <= lvf) << (i == n ? '\n' : ' ');
    }

    return 0;
}

后记

这个题目的重点在于最终状态的寻找,牢记贪心算法老是与数学规律挂钩(尤为是构造)

相关文章
相关标签/搜索