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

Codeforces Round #670 (Div. 2)

傳送門

A. Subset Mex

貪心。

B. Maximum Product

負數的情況只可能為0個,2個或者4個,列舉一下這所有的情況然後取個最大值就行。

首先容易發現一顆樹至多兩個重心。

一個重心的情況好處理,任意新增一條邊即可。
兩個重心的情況對應著一條邊為橋,並且左右兩部分的點數相同。這種情況只需要任選一個葉子結點,然後將其新增入這條邊的另外一個端點即可。易證不會再一次出現兩個重心的情況。

Code
// Author : heyuhhh
// Created Time : 2020/09/12 22:07:43
#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<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> size(n), up(n);
    int Max = INF, x = -1, y = -1;
    function<void(int, int)> dfs = [&] (int u, int fa) {
        up[u] = fa;
        size[u] = 1;
        for (auto& v : G[u]) if (v != fa) {
            dfs(v, u);
            size[u] += size[v];
        }
        int tot = 0;
        int maxv = 0;
        for (auto& v : G[u]) if (v != fa) {
            tot += size[v];
            maxv = max(maxv, size[v]);
        }
        maxv = max(maxv, n - 1 - tot);
        if (Max > maxv) {
            Max = maxv;
            x = u, y = -1;
        } else if (Max == maxv) {
            y = u;
        }
    };
    dfs(0, -1);
    if (n & 1 || y == -1) {
        cout << 2 << ' ' << up[1] + 1 << '\n';
        cout << 2 << ' ' << up[1] + 1 << '\n';
        return;
    }
 
    int lf;
    for (auto v : G[x]) {
        if (v != y) {
            lf = v;
            break;
        }
    }
 
    cout << lf + 1 << ' ' << up[lf] + 1 << '\n';
    cout << lf + 1 << ' ' << y + 1 << '\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;
}

D. Three Sequences

問題轉化過後直接維護 \(a\) 的差值就行,只有非負的差值會產生貢獻。

Code
// Author : heyuhhh
// Created Time : 2020/09/13 09:42:29
#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<ll> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    vector<ll> d(n);
    ll sum = 0;
    for (int i = 1; i < n; i++) {
        d[i] = a[i] - a[i - 1];
        if (d[i] >= 0) 
            sum += d[i];
    }
    auto get = [&] () {
        ll res = sum + a[0];
        if (res > 0)
            return (res + 1) / 2;
        return res / 2;
    };
    auto add = [&] (int p, int v) {
        if (d[p] > 0) sum -= d[p];
        d[p] += v;
        if (d[p] > 0) sum += d[p];
    };
    cout << get() << '\n';
    int q;
    cin >> q;
    while (q--) {
        int l, r, x;
        cin >> l >> r >> x;
        --l, --r;
        if (l)
            add(l, x);
        else 
            a[0] += x;
        if (r + 1 < n) 
            add(r + 1, -x);
        cout << get() << '\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;
}

E. Deleting Numbers

題意:
互動題。
一開始給定一個數 \(n\),會產生一個 \(\{1,2,\cdots,n\}\)這樣的集合。然後要猜測一個數 \(x,x\leq n\)
之後可以執行不超過 \(10000\) 次操作,操作有三種:

  • \(A\ a\):表示詢問集合中有多少個數為 \(a\) 的倍數;
  • \(B\ b\):表示詢問集合中有多少個數為 \(a\) 的倍數,並且在集合中把這些數刪去。
  • \(C\ x\),用於回答 \(x\) 是多少。

最後就是通過第三種操作回答 \(x\)

思路:
因為 \(n\leq 10^5\),所以 \(x\) 至多有一個 \(\geq 330\)

的質因子,並且打表可以發現這個範圍內大概有 \(9300\) 多個質因子,和詢問總次數比較接近。

那麼對於較小的那些質因子,顯然可以直接暴力詢問來計算,比如先詢問 \(B\ p\),再詢問一次 \(A\ p\)看個數是否為 \(0\),如果不為 \(0\) 說明 \(p\) 為其中一個質因子,然後再列舉 \(p\) 的次冪來詢問。

對於較大的質因子,樸素的想法就是對每個質因子類似於剛剛那樣詢問兩次,但顯然總的操作次數不允許。注意到這其實是一個數量變化的關係,並且每次至多變化 \(1\),那麼其實可以對質因子進行分塊,也就是說用 \(A\) 操作來統計總的個數,然後在 \(B\) 操作了一定數量的質因子過後,再通過 \(A\) 操作來統計次數。這樣就能快速的知道質因子存在於哪一塊,把總的詢問次數降下來了。

注意還有一個細節,就是處理完較小的質因子過後,如果當前答案為 \(1\),那麼直接上面這樣做是沒問題的。但是如果答案不為 \(1\),直接上面那樣做可能會有問題。因為比如說 \(n=x=6\),那麼我處理較大質因子時數量的變化是正確的,因為每次我只會刪掉當前這個質數,但是不能找到答案。所以這種情況需要特殊處理。
詳見程式碼:

Code
// Author : heyuhhh
// Created Time : 2020/09/13 10:20:40
#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;
 
int primes[N], tot;
bool vis[N];
void init(int n) {
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            primes[++tot] = i;
        }
        for (int j = 1; j <= tot && primes[j] * i <= n; j++) {
            vis[primes[j] * i] = true;
            if (i % primes[j] == 0) {
                break;
            }
        }
    }
}
 
void run() {
    int n;
    cin >> n;
    init(n);
    int t = 1;
    while (t * t <= n) ++t;
 
    auto query = [&] (ll x, int op = 1) {
        if (x > n) return 0;
        if (op == 1)
            cout << "B " << x << endl;
        else 
        	cout << "A " << x << endl;
        cout << endl;
        int y; cin >> y;
        return y;
    };
 
    int p = 1;
    int ans = 1;
    for (; p <= tot && primes[p] < t; p++) {
    	query(primes[p]);
        int x = query(primes[p]);
        if (x) {
            int now = primes[p] * primes[p];
            for (int i = 2; now / primes[p] <= n; now *= primes[p]) {
                if (!query(now)) {
                    ans *= now / primes[p];
                    break;
                }
            }
        }
    }
    if (ans != 1) {
        for (; p <= tot; p++) {
            int x = query((ll)ans * primes[p]);
            if (x > 0) {
                ans *= primes[p];
                break;
            }
        }
    } else {
        int B = 95;
        int last = query(1, 2);
        for (int cnt = 0; p <= tot; ++p) {
            query(primes[p]);
            if (++cnt == B || p == tot) {
                int now = query(1, 2);
                if (last - now != cnt) {
                    for (int i = p - cnt + 1; i <= p; i++) {
                        int x = query(primes[i]);
                        if (x) {
                            cout << "C " << ans * primes[i] << endl;
                            return;
                        }
                    }
                }
                last = now;
                cnt = 0;
            }
        }
    }
 
    cout << "C " << ans << endl;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}