1. 程式人生 > 實用技巧 >Codeforces Round #669 (Div. 2)

Codeforces Round #669 (Div. 2)

傳送門

A. Ahahahahahahahaha

題意:
給定一個長度為 \(n,n\leq 10^3\)並且 \(n\) 為偶數的 \(01\) 序列。
現在去掉最多 \(\frac{n}{2}\) 個元素,使得剩下的序列奇數位置的和減去偶數位置的和為 \(0\)

思路:
如果 \(0\) 的個數超過一半,那麼留下所有 \(0\) 即可。
否則要考慮留下一些 \(1\),並且這些 \(1\) 一定是成對出現的。考慮連續的三個數只有可能這幾種情況:\(001,010,011\),始終是能夠選出一對出來,也就是每三個數會選兩個出來,那麼直接這樣選直到滿足條件即可。
題解的做法是根據大小關係直接構造全 \(0\) 或者全 \(1\)

Code
// Author : heyuhhh
// Created Time : 2020/09/08 22:50:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    vector<int> b;
 
    for (int i = 0; i < n; i++) {
        int j = i + 1, k = i + 2;
        if (j < n && a[i] == a[j]) {
            ++i;
            b.emplace_back(a[i]);
            b.emplace_back(a[i]);
        } else if (j < n && k < n) {
            i = k;
            b.emplace_back(a[i]);
            b.emplace_back(a[i]);
        } else if (j == n) {
            if (a[i] == 0)
                b.emplace_back(a[i]);
        } else if (k == n) {
            b.emplace_back(0);
            break;
        }
    }
 
    cout << sz(b) << '\n';
    for (auto it : b)
        cout << it << ' ';
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

B. B. Big Vova

貪心構造就行。時間複雜度 \(O(n^2logn)\)

Code
// Author : heyuhhh
// Created Time : 2020/09/08 22:39:15
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) 
        cin >> a[i];
    sort(a.rbegin(), a.rend());
    vector<int> b(n);
    vector<bool> chk(n);
    int g = 0;
    for (int i = 0; i < n; i++) {
        int now = -1, p;
        for (int j = 0; j < n; j++) if (!chk[j]) {
            if (__gcd(g, a[j]) > now) {
                now = __gcd(g, a[j]);
                p = j;
            }
        }
        chk[p] = true;
        b[i] = a[p];
        g = __gcd(g, a[p]);
    }
    for (int i = 0; i < n; i++)
        cout << b[i] << " \n"[i == n - 1];
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. Chocolate Bunny

題意:
互動題。
現在有一個排列 \(p\),但是隻知道長度。
然後最多 \(2n\) 次詢問,每次詢問 \((x,y)\),會回答 \(p_x\% p_y\) 的值。
最後要還原排列。

思路:
詢問 \((x,y),(y,x)\) 即可得到較小的那個數,也就是每兩次詢問能確定一個數,那麼通過 \(2(n-1)\) 次詢問能夠確定 \(n-1\) 個數,剩下一個數可以直接確定。

D. Discrete Centrifugal Jumps

題意:
現在有 \(n\) 根柱子,第 \(i\) 根的高度為 \(h_i\)
現在能從 \(i\) 跳到 \(j\),當且僅當:

  • \(j=i+1\);
  • \(max(h_{i+1},\cdots,h_{j-1})<min(h_i,h_j)\);
  • \(min(h_{i+1},\cdots,h_{j-1})<max(h_i,h_j)\).

問最少多少跳能從 \(1\)\(n\)

思路:
第一種情況不用考慮,主要來考慮第二、三種情況:
對於一個 \(i\),能跳到的位置一定是一個遞增的序列,並且最右邊的位置是第一個高度大於等於 \(h_i\) 的位置,任意證明再往後就不合法了。除開最後一項,前面的一定要滿足他們的高度要小於 \(h_i\),即 \(h_i\) 是第一個高度大於他們的。
第三種情況就是一個遞減序列,最右邊是第一個小於等於 \(h_i\) 的位置,並且 \(h_i\) 是第一個小於他們的。
但只有小於會出問題,因為可能出現多個數相等的情況,這樣只能從前面第一個小於等於/大於等於的地方轉移過來。
通過單調棧預處理這些資訊,然後直接 \(dp\) 即可,轉移是常數級的,所以複雜度為 \(O(n)\)

Code
// Author : heyuhhh
// Created Time : 2020/09/09 00:21:00
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    vector<int> sta(n);
    int top = -1;
    vector<int> big(n);
    for (int i = 0; i < n; i++) {
        while (top >= 0 && a[sta[top]] <= a[i])
            big[sta[top--]] = i;
        sta[++top] = i;
    }
    while (top >= 0) 
        big[sta[top--]] = -1;
    vector<int> small(n);
    for (int i = 0; i < n; i++) {
        while (top >= 0 && a[sta[top]] >= a[i])
            small[sta[top--]] = i;
        sta[++top] = i;
    }
    while (top >= 0)
        small[sta[top--]] = -1;
    
    vector<int> lbig(n);
    for (int i = n - 1; i >= 0; i--) {
        while (top >= 0 && a[sta[top]] <= a[i])
            lbig[sta[top--]] = i;
        sta[++top] = i;
    }
    while (top >= 0) 
        lbig[sta[top--]] = -1;
    vector<int> lsmall(n);
    for (int i = n - 1; i >= 0; i--) {
        while (top >= 0 && a[sta[top]] >= a[i])
            lsmall[sta[top--]] = i;
        sta[++top] = i;
    }
    while (top >= 0)
        lsmall[sta[top--]] = -1;
    
    vector<vector<int>> G(n);
    for (int i = 0; i < n; i++) {
        if (lsmall[i] != -1)
            G[i].push_back(lsmall[i]);
        if (lbig[i] != -1)
            G[i].push_back(lbig[i]);
        if (small[i] != -1)
            G[small[i]].push_back(i);
        if (big[i] != -1)
            G[big[i]].push_back(i);
    }
    vector<int> dp(n, INF);
    dp[0] = 0;
    for (int i = 1; i < n; i++) {
        for (auto j : G[i]) {
            dp[i] = min(dp[i], dp[j] + 1);
        }
    }
    int res = dp[n - 1];
    cout << res << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

題意:
給定一個 \(n\) 個點,\(m\) 條邊的有向圖,現在要給每個結點黑白染色,從一個點出發,只能走對應顏色的出邊。
現在問怎麼染色能使得 \(1\rightarrow n\) 的最短路儘可能長。

思路:
樸素想法即是從起點開始貪心染色,選擇一個距離最短的出點,然後將當前點染為和該邊顏色不同的顏色,這樣就能避免走最短路。但是實際上程式碼中很複雜,因為可能會涉及回溯等問題。
實際上直接在反向圖上面染色即可,從終點往前bfs進行染色。
假設當前點為 \(v\),那麼對於 \((u,v,t)\) 這樣的邊,如果 \(u\) 點未染色就染為另一種顏色,否則距離加 \(1\) 並且入隊。這樣能保證最優性,因為假設存在另外一點 \(v'\) 能夠到 \(u\),但是在後面出隊,說明距離更大,那麼染色過後最短路可以從 \(u\rightarrow v\) 這條路過來。
執行一遍上述演算法即可得到最長的最短距離以及染色方案。

Code
// Author : heyuhhh
// Created Time : 2020/09/09 11:25:07
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n, m;
    cin >> n >> m;
    vector<vector<pii>> G(n);
    for (int i = 0; i < m; i++) {
        int u, v, t;
        cin >> u >> v >> t;
        --u, --v;
        G[v].emplace_back(u, t);
    }
    vector<int> col(n, -1), dis(n, INF);
    vector<bool> vis(n);
    queue<int> q;
    dis[n - 1] = col[n - 1] = 0;
    q.push(n - 1);
    while (!q.empty()) {
        int v = q.front(); q.pop();
        vis[v] = true;
        for (auto& it : G[v]) {
            int u = it.fi, t = it.se;
            if (vis[u]) continue;
            if (col[u] == -1) {
                col[u] = 1 - t;
            } else {
                if (col[u] == t && dis[u] > dis[v] + 1) {
                    dis[u] = dis[v] + 1;
                    q.push(u);
                }
            }
        }
    }
    int ans = dis[0];
    if (ans == INF) ans = -1;
    cout << ans << '\n';
    for (int i = 0; i < n; i++)
        cout << max(0, col[i]);
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}