【解題報告】Codeforces Round #371 (Div. 2)

題目鏈接


A. Meeting of Old Friends(Codeforces 714A)

思路

首先我們可以討論兩個區間的位置關係(內含,相交,相等或相離),相離情況下答案爲 0 ,不相離的情況下求出這兩個區間的公共部分,然後判斷那個間斷時間點在不在公共部分就能求出結果了。
比較巧妙的辦法是,令 [L,R]=[max(l1,l2),min(r1,r2)] ,這實際上就是兩個區間的公共部分(若這個區間的右端點在左端點的左邊,就表示題給兩個區間是相離的)。然後判斷 k 是否在 [L,R] 中,如果在則答案爲 RL ,否則爲 RL+1
(以下的代碼用的是直接分類討論的方法)

代碼

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

long long l1, r1, l2, r2, k, L, R;

int main() {
    cin >> l1 >> r1 >> l2 >> r2 >> k;、
    // 區間相離
    if(r1 < l2 || r2 < l1) {
        cout << 0;
    }
    // 區間相等或區間2包含區間1
    else if(l1 >= l2 && r1 <= r2) {
        cout << r1 - l1 + (l1 > k || k > r1);
    }
    // 區間相交
    else if(l2 > l1 && r2 < r1) {
        cout << r2 - l2 + (l2 > k || k > r2);
    }
    // 區間相交
    else {
        L = max(l1, l2);
        R = min(r1, r2);
        cout << R - L + (L > k || k > R);
    }
    return 0;
}

B. Filya and Homework(Codeforces 714B)

思路

我們先統計數組中不同數的個數是多少(假設它爲 m )。然後可以根據 m 分類討論:

  • m=1m=2 。此時一定能讓所有的數相等。
  • m=3 。若最大的數減第二大的數等於第二大的數減去最小的數則能夠讓所有數相等,否則不能。
  • m4 。此時一定無法讓所有的數相等。

代碼

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

const int maxn = 1e5 + 10;
int n, m, a[maxn];

int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    sort(a, a + n);
    m = unique(a, a + n) - a;
    if(m >= 4) {
        puts("NO");
    }
    else if(m == 1 || m == 2) {
        puts("YES");
    }
    else {
        puts(a[2] - a[1] == a[1] - a[0] ? "YES" : "NO");
    }
    return 0;
}

C.Sonya and Queries(Codeforces 714C)

思路

首先,我們可以將輸入的數字當成 01 字符串,其中奇數對應的位爲 1 ,否則爲 0 。如果不滿 18 位則在 01 字符串的開頭補 0 使其滿 18 位。
然後,每讀入一個操作符爲 + 的操作數,就將其按照前面講的方式變成 01 字符串並插入 Trie 中,將該字符串的權值根據操作符的不同加 1(+) 或減 1()
當操作符爲 時,在 Trie 數中查找代表操作數的字符串,其權值爲要輸出的值。權值爲 0 或字符串不存在都輸出 0
(賽後發現有更簡單的方法,就是直接將 01 字符串壓縮成整數 k ,用 num[k] 表示 k 對應的數在多重集中有幾個)

代碼

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

const int maxNode = 2e6, sigmaSize = 5;

// Trie的模板
struct Trie {
    int ch[maxNode][sigmaSize];
    int val[maxNode];
    int sz;
    Trie() {
        sz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
    }
    int idx(char c) {
        return c - '0';
    }
    void insert(string s, int v) {
        int u = 0, n = s.size();
        for(int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if(!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] += v;
    }
    int find(string s) {
        int u = 0, n = s.size();
        for(int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if(!ch[u][c]) {
                return 0;
            }
            u = ch[u][c];
        }
        return val[u];
    }
}o;

int n;
string s, t, opt;

int main() {
    ios_base::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> opt >> s;
        // 處理成0-1字符串
        for(int i = 0; i < s.size(); i++) {
            s[i] = (s[i] - '0') % 2 ? '1' : '0';
        }
        string t;
        // 在首部添0
        for(int i = 1; i <= 18 - s.size(); i++) {
            t.push_back('0');
        }
        s = t + s;
        if(opt != "?") {
            o.insert(s, opt == "+" ? 1 : -1);
        }
        else {
            cout << o.find(s) << endl;
        }
    }
    return 0;
}

E. Sonya and Problem Wihtout a Legend(Codeforces 714E)

思路

先來思考一個簡單的問題。當題目中的“嚴格單調遞增”改成“單調非遞減”時,問題該怎樣解決。
用搜索解決肯定是複雜度太高了,如果用動態規劃呢?我們先嚐試劃分階段。假設我們定義階段i爲考慮到第 i 個數字爲止的數組的前綴。那麼理論上如果我們知道第 i1 個階段下,第 i1 個數字被修改爲x的最小修改次數,就能知道第 i 個階段下,第 i 個數字被修改爲 y(xy) 的最小修改次數。
於是我們定義 d[i][j] 爲當考慮到第 i 個數字且第 i 個數字被修改爲j時的最小修改次數。那麼有如下狀態轉移方程成立

d[i][j]=min{d[i1][k]+a[i]j,jk}

現在似乎按照方程遞推就能得解了。但是此處還有兩個問題需要解決。第一, O(n3) 的複雜度還是太高了。這個問題可以通過建立前綴數組來解決,具體就是令 Min[j]mind[i1][k],jk 。這樣方程就變成了 d[i][j]=Min[j]+a[i]j ,複雜度爲 O(n2) 。第二, a[i] 的上界爲 109 ,那麼 d[i][j] 將會很大。這個問題可以通過將數組 a 離散化來解決。這樣空間複雜度也被優化到 O(n2) 了。
這樣以來整個問題就解決了。但是原問題似乎還沒得到解決。事實上,我們可以簡單地令所有的 a[i]=a[i]i 。就能將問題轉化成我們剛剛討論的問題,這樣原問題也順利得解了。

代碼

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

typedef long long ll;
const int maxn = 3010;
int n, m, a[maxn], b[maxn];
ll ans, Min[maxn], d[maxn][maxn];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        a[i] = a[i] - i;
        b[i] = a[i];
    }
    // 離散化中的排序
    sort(b + 1, b + n + 1);
    // 離散化中的去重
    m = unique(b + 1, b + n + 1) - b - 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            d[i][j] = Min[j] + abs(b[j] - a[i]);
        }
        Min[1] = d[i][1];
        // 處理前綴最小值數組
        for(int j = 2; j <= m; j++) {
            Min[j] = min(d[i][j], Min[j-1]);
        }
    }
    printf("%I64d\n", Min[m]);
    return 0;
}

(其它題目略)

發佈了70 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章