1. 程式人生 > 實用技巧 >Educational Codeforces Round 95 (Rated for Div. 2)

Educational Codeforces Round 95 (Rated for Div. 2)

傳送門

A. Buying Torches

簽到。

Code
// Author : heyuhhh
// Created Time : 2020/09/14 22:39:56
#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() {
    ll x, y, k;
    cin >> x >> y >> k;
    ll a = (k * (1 + y) - 1 + x - 2) / (x - 1);
    ll ans = a + k;
    cout << ans << '\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. Negative Prefixes

貪心選即可。

C. Mortal Kombat Tower

直接dp就可,一維狀態表示當前到第幾只boss,再加一維表示誰的回合。然後列舉所有情況轉移。

Code
// Author : heyuhhh
// Created Time : 2020/09/14 23:00:27
#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 chkmin(int& x, int y) {
    if (x > y) x = y;
}
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<vector<int>> dp(n + 1, vector<int>(2, INF));
    dp[0][0] = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 2; j++) if (dp[i][j] != INF) {
            if (j == 0) {
                chkmin(dp[i + 1][1], dp[i][j] + a[i]);
                if (i + 2 <= n) {
                    chkmin(dp[i + 2][1], dp[i][j] + a[i] + a[i + 1]);
                }
            } else {
                chkmin(dp[i + 1][0], dp[i][j]);
                if (i + 2 <= n) {
                    chkmin(dp[i + 2][0], dp[i][j]);
                }
            }
        }
    }
    int ans = min(dp[n][0], dp[n][1]);
    cout << ans << '\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. Trash Problem

仔細一想其實很簡單,答案就為最大值-最小值-差值的最大值。
然後用set什麼的維護一下就行。

Code
// Author : heyuhhh
// Created Time : 2020/09/14 23:29:47
#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, q;
    cin >> n >> q;
    vector<int> a(n);
    for (int i = 0; i < n; i++) 
        cin >> a[i];
    sort(all(a));
    set<int> s;
    multiset<int> t;
    int last = -1;
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        int p = a[i];
        s.insert(p);
        if (last != -1) {
            t.insert(p - last);
            ans += p - last;
        }
        last = p;
    }
    cout << ans - (sz(t) > 0 ? *t.rbegin() : 0) << '\n';
    while (q--) {
        int op, x;
        cin >> op >> x;
        if (op) {
            auto it = s.lower_bound(x);
            int prev = -1, succ = -1;
            if (it != s.end()) {
                succ = *it;
                t.insert(succ - x);
                ans += succ - x;
            }
            if (it != s.begin()) {
                --it;
                prev = *it;
                t.insert(x - prev);
                ans += x - prev;
            }
            s.insert(x);
            if (prev != -1 && succ != -1) {
                t.erase(t.lower_bound(succ - prev));
                ans -= succ - prev;
            }
        } else {
            auto it = s.upper_bound(x);
            int prev = -1, succ = -1;
            if (it != s.end()) {
                succ = *it;
                t.erase(t.lower_bound(succ - x));
                ans -= succ - x;
            }
            --it;
            if (it != s.begin()) {
                --it;
                prev = *it;
                t.erase(t.lower_bound(x - prev));
                ans -= x - prev;
            }
            s.erase(x);
            if (prev != -1 && succ != -1) {
                t.insert(succ - prev);
                ans += succ - prev;
            }
        }
        cout << ans - (sz(t) > 0 ? *t.rbegin() : 0) << '\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. Expected Damage

題意:
\(n\) 只怪物,每隻的攻擊力為 \(d_i\)
現在有一個防護盾,耐久度為 \(a\),防禦力為 \(b\)
現在怪物會隨機進行攻擊,每個怪物只會攻擊一次,規則如下:

  • 如果 \(a=0\),那麼直接造成傷害;
  • 如果 \(a>0\) 並且 \(d_i\geq b\),那麼耐久度減一;
  • 否則什麼都沒發生。

現在問所有怪物隨機攻擊的情況下,期望造成多少傷害。

思路:
根據期望的線性性質,考慮每隻怪物期望造成的傷害值,那麼加起來就為答案。
我們按照攻擊力將怪物分為兩類,兩類分別來進行處理。
之後考慮求概率,為成功擊中的概率,攻擊力較大的怪物的概率為 \(1-\frac{a}{big}\);攻擊力較小的怪物的概率為 \(1-\frac{a}{big+1}\)。然後直接算期望就行。
這裡的概率的計算其實是利用了條件概率的性質。如果各個變數相互獨立的情況下,那麼我們可以根據某幾個特定元素之間的關係來計算出概率,只用考慮我們關注的東西即可。

Code
// Author : heyuhhh
// Created Time : 2020/09/15 00:14: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 = 2e5 + 5, MOD = 998244353;
 
int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
 
void run() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    sort(all(a));
    vector<ll> pre(n), suf(n);
    pre[0] = a[0];
    for (int i = 1; i < n; i++)
        pre[i] = (pre[i - 1] + a[i]) % MOD;
    suf[n - 1] = a[n - 1];
    for (int i = n - 2; i >= 0; i--) 
        suf[i] = (suf[i + 1] + a[i]) % MOD;
 
    for (int i = 0; i < m; i++) {
        int A, B;
        cin >> A >> B;
        int t = lower_bound(all(a), B) - a.begin();
        int tot = n - t;
        if (tot < A) {
            cout << 0 << '\n';
            continue;
        }
        int ans = 1ll * (1 - 1ll * A * qpow(tot, MOD - 2) % MOD + MOD) % MOD * suf[t] % MOD;
        if (t) {
            ans = (ans + 1ll * pre[t - 1] * (1 - 1ll * A * qpow(tot + 1, MOD - 2) % MOD + MOD) % MOD) % MOD;
        }
        cout << ans << '\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;
}

G. Three Occurrences

題意:
給定一個序列 \(a\),問有多少個區間 \([l,r]\),滿足 \(a_l,a_{l+1},\cdots,a_r\) 中的數剛好出現三次。

思路:
問題等價於每個區間中,所有數要麼出現 \(0\) 次要麼出現 \(3\) 次。
其實可以單獨考慮每個數,考慮一個數的情況下,對於每個固定的左端點,右端點哪些合法。手模一下會發現合法的是兩段區間,一段是出現 \(0\) 次的區間,一段是出現 \(3\) 次的區間。
那麼我們隨著左端點的移動,每次只會新增入/刪除一個數,也就會影響常數個區間,那麼直接用線段樹來維護所有合法的區間即可。

具體來說,我們對於每個數,用 \(0\) 表示該端點作為右端點合法,\(1\) 則不合法。那麼當左端點列舉到 \(l\) 時,假設已經更新完,那麼所有和為 \(0\) 的位置就是合法的右端點。那麼直接線段樹維護最小值記其個數即可。為了方便起見,左端點從右往左進行移動,並且在每個維護值出現位置的vector後面先新增一個 \(n+1\) 作為哨兵結點。
細節見程式碼:

Code
// Author : heyuhhh
// Created Time : 2020/09/15 19:23:18
#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 = 5e5 + 5;
 
int n;
int a[N];
int minv[N << 2], cntv[N << 2], lz[N << 2];
void tag(int o, int l, int r, int v) {
    minv[o] += v;
    lz[o] += v;
}
 
void push_up(int o) {
    minv[o] = min(minv[o << 1], minv[o << 1|1]);
    cntv[o] = 0;
    if (minv[o << 1] == minv[o]) cntv[o] = cntv[o << 1];
    if (minv[o << 1|1] == minv[o]) cntv[o] += cntv[o << 1|1];
}
 
void push_down(int o, int l, int r) {
    if(lz[o] != 0) {
        int mid = (l + r) >> 1;
        tag(o << 1, l, mid, lz[o]);
        tag(o << 1|1, mid + 1, r, lz[o]);
        lz[o] = 0;   
    }
}
 
void build(int o, int l, int r) {
    lz[o] = 0;
    if(l == r) {
        minv[o] = 0;
        cntv[o] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid), build(o << 1|1, mid + 1, r);
    push_up(o);
}
 
void update(int o, int l, int r, int L, int R, ll v) {
    if(L <= l && r <= R) {
        tag(o, l, r, v);
        return;
    }   
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    if(L <= mid) update(o << 1, l, mid, L, R, v);
    if(R > mid) update(o << 1|1, mid + 1, r, L, R, v);
    push_up(o);
}
 
int query(int o, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        if (minv[o] == 0) return cntv[o];
        return 0;
    }
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    int res = 0;
    if(L <= mid) res = query(o << 1, l, mid, L, R);
    if(R > mid) res += query(o << 1|1, mid + 1, r, L, R);
    return res;
}
 
void run() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    vector<vector<int>> pos(n + 1);
    build(1, 1, n);
 
    for (int i = 1; i <= n; i++) {
        pos[i].emplace_back(n + 1);
    }
    auto Add = [&] (int x, int v) {
        int third = pos[a[x]][sz(pos[a[x]]) - 3];
        int fourth = pos[a[x]][sz(pos[a[x]]) - 4];
        update(1, 1, n, third, fourth - 1, v);
    };
    ll ans = 0;
    for (int l = n; l >= 1; l--) {
        if (sz(pos[a[l]]) >= 4) {
            Add(l, 1);
        }
        pos[a[l]].emplace_back(l);
        if (sz(pos[a[l]]) >= 4) {
            Add(l, -1);
        }
        int second = pos[a[l]][sz(pos[a[l]]) - 2];
        update(1, 1, n, l, second - 1, 1);
        ans += query(1, 1, n, l, n);
    }
    cout << ans << '\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;
}