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;
}

後記

這個題目的重點在於最終狀態的尋找,牢記貪心算法老是與數學規律掛鉤(尤爲是構造)

相關文章
相關標籤/搜索