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

Codeforces Round #668 (Div. 1)

傳送門

A. Balanced Bitstring

題意:
給一個01串,其中某些位置為'?',即可以為0也可以為1。
並且會給出一個 \(k\)\(k\) 是偶數,現在問是否可以使得所有長度為 \(k\) 的子串中都包含相等的0和1。

思路:
顯然可以發現該串存在一個長度為 \(k\) 的迴圈節。
也就是說每隔 \(k\) 個位置的數一定相等,根據這一點來做就好了。

Code
// Author : heyuhhh
// Created Time : 2020/09/16 11:04:53
#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, k;
    cin >> n >> k;
    string s;
    cin >> s;
    for (int i = 0; i < k; i++) {
        bool has_0 = false, has_1 = false;
        for (int j = i; j < n; j += k) {
            if (s[j] == '0') has_0 = true;
            if (s[j] == '1') has_1 = true;
        }
        if (has_0 && has_1) {
            cout << "NO" << '\n';
            return;
        }
        if (has_0) {
            for (int j = i; j < n; j += k) {
                s[j] = '0';
            }
        }
        if (has_1) {
            for (int j = i; j < n; j += k) {
                s[j] = '1';
            }
        }
    }
 
    vector<int> cnt(2);
    for (int i = 0; i < k; i++) {
        if (s[i] == '0') ++cnt[0];
        else if (s[i] == '1') ++cnt[1];
    }
    if (cnt[0] <= k / 2 && cnt[1] <= k / 2) {
        cout << "YES" << '\n';
    } else {
        cout << "NO" << '\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. Tree Tag

題意:
給定一顆 \(n\) 個點的無根樹,現在有兩個人分別位於結點 \(a\)\(b\)。現在第一個人每次最多能跳 \(da\) 距離,第二個人最多能跳 \(db\) 距離。
如果存在某一時刻兩個人位於同一個點,那麼第一個人就win;否則第二個人win。

思路:
題意就是第一個人要抓第二個人,第二個人就負責跑。
我們可以分情況來做做:

  • 如果 \(A\) 上來就能抓住顯然就沒了;
  • \(2\cdot da\geq L\)\(L\) 為樹的直徑。這顯然 \(A\) 只需要跳到樹的重心,之後便可跳到任意一個位置;
  • 現在一定存在死角,假設 \(B\) 位於死角,\(A\)
    之後一定會跳過來,假設 \(A\) 下一步就能抓住 \(B\) 了,此時又分情況:
    • \(db\leq 2 * da\),那 \(B\) 鐵定跑不掉;
    • 否則 \(B\) 還是有機會跑掉的,這個較為顯然,注意現在的情況存在一個前提:\(L>da\ \&\&\ db>2*da\)

就分上面幾種情況來做,還是有點細節。

Code
// Author : heyuhhh
// Created Time : 2020/09/07 09:29:10
#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, a, b, da, db;
    cin >> n >> a >> b >> da >> db;
    --a, --b;
    vector<vector<int>> G(n);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> dep(n), up(n);
    int L = 0, rt = 0;
    function<void(int, int)> dfs = [&] (int u, int fa) {
        up[u] = fa;
        if (dep[u] > L) {
            L = dep[u];
            rt = u;
        }
        for (auto v : G[u]) if (v != fa) {
            dep[v] = dep[u] + 1;
            dfs(v, u);
        }
    };
    dfs(0, -1);
    dep[rt] = 0;
    int node = rt;
    dfs(rt, -1);
    int x = a, y = b;
    if (dep[a] < dep[b]) 
        swap(a, b);
    while (dep[a] > dep[b]) 
        a = up[a];
    while (a != b)
        a = up[a], b = up[b];
    int dis = dep[x] + dep[y] - 2 * dep[a];
    if (da >= dis || min(L, db) <= 2 * da) {
        cout << "Alice" << '\n';
    } else {
        cout << "Bob" << '\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;
}

C. Fixed Point Removal

題意:
給定一個序列 \(a\),現在可以執行若干次下面的操作:

  • \(a_i=i\),那麼可以刪除 \(a_i\),後面的元素往前移動。

然後回答若干組詢問,每組詢問為一段區間,表示只能刪除區間 \([l,r]\) 中的數。
對於每組詢問回答最多能夠執行多少次上述操作。

思路:
一個較為顯然的想法是令 \(a_i=i-a_i\),那麼每次能夠刪除一個為 \(0\) 的位置,後面的元素都減去 \(1\)
如果沒有詢問的話那麼就是一個貪心,我們每次刪除最後面的那個 \(0\) 就行。
但現在有詢問就比較難搞,仔細思考其實可以注意到如果固定左端點,我們不斷增加右端點的話就很好做,具體來說假設當前 \([l,r]\) 的答案為 \(x\),之後右端點要增加 \(1\)。那麼如果 \(0\leq a_{r+1}\leq x\)\([l,r+1]\) 的答案將變為 \(x+1\)。我們不用管是如何操作的,但是最後最優方法一定可以使得答案增加 \(1\)

但是現在有很多個區間,左端點還會移動,這樣不好判斷。

我們還是考慮從左往右移動右端點,用一個數組比如 \(f[i]\) 表示左端點為 \(i\) 時的答案。容易發現 \(f[1]\geq f[2]\geq\cdots\geq f[r]\)。那麼根據這一點,我們右端點移動時,會有一個字首增加 \(1\),現在就很容易通過資料結構去維護了。
現在只需將詢問離線,在訪問到詢問區間右端點時統計答案即可。
細節見程式碼:

Code
// Author : heyuhhh
// Created Time : 2020/09/16 15:46: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;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = (1 << 19);
 
struct FT {
    int c[N];
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, int v) {
        for (++x; x < N; x += lowbit(x)) {
            c[x] += v;
        }
    }
    int query(int x) {
        int res = 0;
        for (++x; x; x -= lowbit(x)) {
            res += c[x];
        }
        return res;
    }
    int find(int x) {
        int p = 0;
        for (int i = 18; i >= 0; i--) {
            if (query(p | (1 << i)) >= x) {
                p |= (1 << i);
            }
        }
        return p - 1;
    }
} bit;
 
void run() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        --a[i];
        a[i] = i - a[i];
    }
 
    vector<vector<pii>> Q(n);
    for (int i = 1; i <= q; i++) {
        int x, y;
        cin >> x >> y;
        int l = x, r = n - y - 1;
        Q[r].emplace_back(l, i);
    }
    vector<int> ans(q + 1);
    for (int r = 0; r < n; r++) {
        if (a[r] >= 0) {
            int L = 0, R = r + 1, mid;
            while (L < R) {
                mid = (L + R) >> 1;
                if (bit.query(mid) >= a[r]) {
                    L = mid + 1;
                } else {
                    R = mid;
                }
            }
            // dbg(r, L);
            bit.add(0, 1), bit.add(L, -1);
        }
 
        for (auto& it : Q[r]) {
            int l = it.fi, id = it.se;
            ans[id] = bit.query(l);
        }
    }
    for (int i = 1; i <= q; i++) {
        cout << ans[i] << '\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;
}

D. Game of Pairs

題意:
首先會給定一個 \(n\),然後可以選擇先手或者後手:

  • 如果選擇先手,將 \(1,2,\cdots,2n\) 兩兩分組,後手此時只能從每組裡面選擇一個,如果選出來的數的和是 \(2n\) 的倍數就輸了,否則就贏;
  • 如果選擇後手,那麼會給出來一個類似於上面的分組,現在你要選出一些數,如果這些數的和為 \(2n\) 的倍數那麼就贏了,否則就輸。

現在就問怎麼操作能夠必勝。

思路:
如果 \(n\) 為偶數的話,那麼類似於 \((1,n+1),(2,n+2),\cdots,(n,2n)\) 這樣分組能夠必勝。
原因在於所有組中的兩個數在模 \(n\) 意義下就是相等的。那麼後者必然選出來的數滿足 \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}(mod\ n)\)
分式很難看,令 \(n=2m\),就有 \(m\cdot(2m-1)(mod\ 2m)\)。因為 \(2m-1\) 為奇數,那麼式子顯然不為 \(0\),也就是說不整除 \(n\),顯然也不整除 \(2n\)

如果 \(n\) 為奇數的話,需要下面的觀察:

  • 所有數的和為 \(n\cdot(2n+1)\),其在模 \(2n\) 意義下為\(n\)。那麼如果我選出來的數在模 \(n\) 意義下為 \(0\),就能找到答案。

因為此時如果 \(\% 2n\)\(0\),就是答案;如果為 \(n\),我全部選另外的數,那就是答案。

還有另外一個觀察:

  • \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}\),此時 \(n\) 為奇數,那麼一定是 \(n\) 的倍數。

所以現在我們就選出一些在模 \(n\) 意義下兩兩不同的數即可。
這個是能夠選出來的,我們連邊 \(i\rightarrow i+n\),以及分組中的數互相連邊,那麼最終一定會形成若干個長度為偶數的環。之後對每個環奇偶染色即可。

Code
// Author : heyuhhh
// Created Time : 2020/09/16 18:33:32
#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;
    if (n % 2 == 0) {
        cout << "First" << '\n';
        for (int i = 0; i < 2 * n; i++) {
            if (i) cout << ' ';
            cout << i % n + 1;
        }
        cout << endl << endl;
        int x;
        cin >> x;
    } else {
        cout << "Second" << endl << endl;
        vector<vector<int>> has(n);
        for (int i = 0; i < 2 * n; i++) {
            int bel;
            cin >> bel;
            --bel;
            has[bel].emplace_back(i);
        }
        vector<vector<int>> G(2 * n);
        for (int i = 0; i < n; i++) {
            G[i].emplace_back(i + n);
            G[i + n].emplace_back(i);
        }
        for (int i = 0; i < n; i++) {
            G[has[i][0]].emplace_back(has[i][1]);
            G[has[i][1]].emplace_back(has[i][0]);
        }
        vector<int> col(2 * n, -1);
        vector<int> zeros, ones;
        int res = 0;
        function<void(int, int)> go = [&] (int u, int c) {
            col[u] = c;
            if (c == 0) {
                res = (res + u + 1) % (2 * n);
                zeros.emplace_back(u);
            } else {
                ones.emplace_back(u);
            }
            for (auto& v : G[u]) {
                assert(col[v] != col[u]);
                if (col[v] == -1) {
                    go(v, c ^ 1);
                }
            }
        };
        for (int i = 0; i < 2 * n; i++) {
            if (col[i] == -1) {
                go(i, 0);
            }
        }
        if (res == 0) {
            for (int i = 0; i < sz(zeros); i++) {
                if (i) cout << ' ';
                cout << zeros[i] + 1;
            }
        } else {
            for (int i = 0; i < sz(ones); i++) {
                if (i) cout << ' ';
                cout << ones[i] + 1;
            }
        }
        cout << endl << endl;
        int x;
        cin >> x;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}