Educational Codeforces Round 95 (Rated for Div. 2) [A -- E]

Educational Codeforces Round 95 (Rated for Div. 2)

A. Buying Torches

題目大意

在一個遊戲中你須要作 \(k\) 個火把,其中每一個火把由 \(1\) 個木棍和 \(1\) 個煤炭組成html

遊戲開始時你擁有 \(1\) 個木棍,能夠執行任意次如下兩種操做之一python

  • \(1\) 個木棍換 \(x\) 個木棍
  • \(y\) 個木棍換 \(1\) 個煤炭

請給出製造 \(k\) 個火把最小須要的操做數ios

  • \(2 \leq x \leq 10^9\)c++

  • \(1 \leq y, k \leq 10^9\)算法

math *1000api

思路分析

這題樣例錯了,WA 死我了!!!數據結構

這題的坑點在於 ceil 函數的精度不足以覆蓋這個題目的數據範圍函數

首先,咱們總共須要 k * y + k 個木棍,咱們每次進行操做一能夠得到 x - 1 個木棍。設操做一的操做數爲 cnt學習

,則存在關係:優化

\[f(cnt) = 1 + cnt \cdot (x - 1) \]

故答案爲:

\[ans = \lceil\frac{(k + 1)\cdot y - 1}{x -1}\rceil \]

注意 ceil 應爲: ceil(x, y) = (x + y - 1) / y

代碼

void solve(){
    LL x, y, k;
    cin >> x >> y >> k;
 
    LL need = k * (y + 1) - 1;
    cout << (need / (x - 1)) + k + !(need % (x - 1) == 0) << endl;
}
 
 
int main(){
    int t = read();
    while (t--) solve();
    return 0;
}

有以下注意點:

  • 假如出現了徹底不可預知的錯誤,請考慮精度問題,不要徹底相信 api

B. Negative Prefixes

題目大意

給定一個有 \(n\) 個元素的序列 \(arr\) ,並給定序列 \(arr\) 中各元素是否被鎖定(可否移動,修改)。

請你輸出 \(k\)最小時對應的序列。

其中, \(k\) 值爲:\(\max j \rightarrow where \{ p_j <0 \},p_j = \sum_{i=1}^{j}a_i\),換一句話說,使得序列前綴和小於 \(0\) 的最大索引。若不存在這樣的索引則 \(k = 0\)

  • \(1 \leq n \leq 100\)
  • \(-10^5 \leq arr_i \leq 10^5\)

greedy sortings *1300

思路分析

很簡單,他不須要你輸出 \(k\) 值,只須要你輸出對應的序列。貪心的去想,顯然咱們但願全部未被鎖定的元素能從大到小排

代碼

const int maxn = 1e2 + 50;
int f[maxn], ilock[maxn], can[maxn]; // ilock -> 是否鎖定, can -> 未被鎖定的序列
void solve(){
    int n = read(), cnt = 0;
    for (int i = 0; i < n; ++ i) f[i] = read();
    for (int i = 0; i < n; ++ i) {
        ilock[i] = read();
        if (ilock[i] == 0) can[cnt++] = f[i];
    }
    sort(can, can + cnt, greater<int>());
 
    int pt = 0;
    for (int i = 0; i < n; ++ i) 
        if (!ilock[i]) f[i] = can[pt++];
    
    print(f, n); // 自定義輸出函數
}
 
 
int main(){
    int t = read();
    while (t--) solve();
    return 0;
}

有以下注意點:

  • 沒有看清楚輸出要求就作題了,實際上要求的可能比較想象的更簡單,彆着急!!

C. Mortal Kombat Tower

題目大意

你和你的朋友須要闖關打 boss ,大家有必定數量的無敵火箭炮!,能夠直接秒殺任何 boss。已知有兩種 boss,簡單 boss 和 困難 boss。

大家須要協助經過 \(n\) 關(這意味着大家須要共殺死 \(n\) 個 boss)。你和你的朋友均可以選擇擊殺 \(1\) 或者 \(2\) 個 boss。須要注意的時:

  • 你能夠擊殺全部 boss
  • 你的朋友只能擊殺簡單 boss
  • 你的朋友先進行闖關

請輸出闖關所需的使用無敵火箭炮的最小數量

  • \(1 \leq n \leq 2\cdot 10^5\)

dp greedy 1500

思路分析

好像有貪心算法的題解,可是顯然這題是個裸的 dp。定義 dpdp[i][j] := 第 i 關由 j 完成的最小開銷,其中 j 只有 0 or 1 表明你和你的朋友。

代碼

const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 50;
int dp[maxn][2], a[maxn];
void solve(){
    int n = read();
    for (int i = 1; i <= n; ++ i) a[i] = read();
    dp[0][0] = dp[0][1] = 0;
    dp[1][0] = a[1], dp[1][1] = inf;
    dp[2][0] = a[1] + a[2];
    dp[2][1] = dp[1][0];
    for (int i = 3; i <= n; ++ i){
        dp[i][0] = min(dp[i - 1][1] + a[i], dp[i - 2][1] + a[i] + a[i - 1]);
        dp[i][1] = min(dp[i - 1][0], dp[i - 2][0]);
    }
    cout << min(dp[n][0], dp[n][1]) << endl;
}
 
int main(){
    int t = read();
    while (t--) solve();
    return 0;
}

有如下注意點:

  • dp 思路混亂,代碼過於暴力,不夠優美
  • 不過寫的很快,這是長處
  • 須要學習貪心算法!!

D. Trash Problem

題目大意

Vova 決定打掃房間。能夠將房間表示爲座標軸 OX 。房間裏有 \(n\) 堆垃圾,第 \(i\) 堆的座標是整數 \(p_i\) 。全部樁具備不一樣的座標。

讓咱們將總清理定義爲如下過程。此過程的目標是將全部樁收集至不超過兩個且互不相同的 \(x\) 座標下。爲了實現此目標,Vova 能夠執行幾回(可能爲零)移動。在移動期間,他能夠選擇一些 \(x\),並使用掃帚將全部堆(沒法控制數量)從 \(x\) 移動到 \(x + 1\)\(x-1\)

此外會有 \(q\) 個詢問,共兩種查詢類型:

  • 0 x - 從座標 \(x\) 移除一堆垃圾。確保此時在座標 \(x\) 中有一個樁。
  • 1 x - 將一堆垃圾添加到座標 \(x\) 。能夠確保此時在座標 \(x\) 中沒有樁。

請注意,有時房間中的垃圾堆爲零。

Vova 想知道,在全部詢問前完成總清理和每次詢問後完成總清理的最小移動次數,請注意,總清理實際上並無發生,也不會改變堆的狀態。它僅用於計算移動次數。

  • \(1 \leq n, q \leq 10^5\)
  • \(1 \leq p_i \leq 10^9\)

data structures hashing *2100

思路分析

題目賊長 ,實際上意思還算比較簡單,須要咱們按照規律進行垃圾清理,保證操做次數最小且符合垃圾清理的要求。給定了 \(q\) 個詢問,且 \(q\) 的數量級還不低,因此確定須要咱們使用較爲高效的數據結構來解決問題。

題目須要咱們將全部堆移動到最多兩個位置,不妨定義 \(x_i, x_j\) 分別爲最後移動的兩個位置且 \(x_i < x_j\)

\[x_{min} \cdots x_i\cdots x_k \cdots x_{k + 1} \cdots x_j \cdots x_{max} \]

上述序列,\(x_{\min}, x_{\max}\) 表明最左最右的堆,定義 \(x_k\) 歸於 \(x_i\) 側,\(x_{k + 1}\) 歸於 \(x_{j}\) 側。因此最終答案爲:

\[ans = x_{\max} - x_{\min} - (x_{k + 1} - x_{k}) \]

通過分析發現,實際上答案能夠定義爲 最大x - 最小x - 最大間隔gap,因爲\(x_{\max} - x_{\min}\) 是固定的,gap 越大答案越小。

所以:本題須要解決,高效獲取序列中最大間隔值(差值),和最大最小值。

固然,本身寫數據結構是不太明智的,咱們能夠經過已有的數據結構例如: set multisetmap進行解決。

代碼

代碼借鑑了紅名大佬的代碼(手上的電腦只能寫 py , 特別麻煩遂做罷)

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, q;
    std::cin >> n >> q;
    
    std::set<int> p; // pos arr
    std::multiset<int> d; // dist arr
    
    // 添加節點
    auto add = [&](int x) {
        auto it = p.insert(x).first;
        auto r = std::next(it);
        
        if (it != p.begin() && r != p.end())
            d.erase(d.find(*r - *std::prev(it)));
        
        if (it != p.begin())
            d.insert(x - *std::prev(it));
        
        if (r != p.end())
            d.insert(*r - x);
    };
    
    // 刪除節點
    auto del = [&](int x) {
        auto it = p.erase(p.find(x));
        
        if (it != p.begin())
            d.erase(d.find(x - *std::prev(it)));
        
        if (it != p.end())
            d.erase(d.find(*it - x));
        
        if (it != p.begin() && it != p.end())
            d.insert(*it - *std::prev(it));
    };
    
    for (int i = 0; i < n; ++i) {
        int x;
        std::cin >> x;
        add(x);
    }
    
    // 返回詢問
    auto get = [&]() {
        if (p.size() <= 1)
            return 0;
        return *p.rbegin() - *p.begin() - *d.rbegin();
    };
    
    std::cout << get() << "\n";
    
    while (q--) {
        int t, x;
        std::cin >> t >> x;
        
        if (t == 0) {
            del(x);
        } else {
            add(x);
        }
        
        std::cout << get() << "\n";
    }
    
    return 0;
}

有以下注意點:

  • 我不熟悉 set 的用法,很致命,須要熟悉 set
  • 不存在 prev(iter) = arr.rbegin() 的用法,其中 rbegin() 是反轉迭代器!!!
  • 這類題目很長的題目每每須要實現的只是一個比較簡單的事情,所以在比賽時請認真讀題。
  • 關於迭代器的操做,我不太熟練,面對不太熟練的題請,先在草稿紙上作好筆記

E. Expected Damage

題目大意

計算指望 \(E\),你擁有一個耐力值爲 \(a\) ,防護值爲 \(b\) 的盾牌,你將遇到 \(n\) 個怪獸,每一個怪獸的力量值爲 \(d_i\)

知足以下規律:

  • 若是 \(a == 0\),你會收到 \(d\) 的傷害。
  • 若是 \(a >0 \land d \geq b\),你不會收到傷害,可是盾牌的耐力值減一:\(a \leftarrow a-1\)
  • 若是 \(a > 0\land d < b\),你不會收到傷害。

總共須要處理 \(m\) 個盾牌的狀況。請計算每一個盾牌給定 \(a_i,b_i\) 下的收到傷害的指望。

其中,預期必定是一個不可約分數 \(\frac{x}{y}\)\(y\)\(998244353\) 互素。你須要輸出 \(x\cdot y^{-1} mod \;998244353\) 的值。其中\(y^{-1}\)\(y\)的逆元。

  • \(1 \leq n,m \leq 2\cdot 10^5\)
  • \(1 \leq a_i \leq n\)
  • \(1 \leq b_i, d_i \leq 10^9\)

probabilities combinatorics binary search *2400

思路分析

首先,怪獸分爲大怪獸和小怪獸兩種,設大怪獸數量爲 \(k\)。當 \(a \ge k\) 時答案爲 \(0\)

對於大怪獸,能形成傷害的機率爲 \(P_1= 1 - \frac{a}{k}\) (最多抵消 \(a\) 個)。

對於小怪獸,咱們能夠將 \(k\) 個大怪獸固定好,所以具備 \(k + 1\) 個間隔,前 \(a\) 個間隔都沒法形成傷害,所以能形成傷害的機率爲:\(P_2= 1 - \frac{a}{k + 1}\)

所以,答案爲:

\[ans = \sum 大怪獸傷害 * P_1+ \sum 小怪獸的傷害 * P_2 \]

對於求和,能夠用二分查找 + 前綴和優化

而後按照費馬小定律求逆元便可。

代碼

import os,io
input=io.BytesIO(os.read(0,os.fstat(0).st_size)).readline # 快讀
import bisect
mod = 998244353

def fpow(a, b):
    ans = 1
    while b:
        if (b & 1): ans = (ans * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    return ans

n, m = map(int, input().split())

d = list(map(int, input().split()))
d = sorted(d)

pre = [0 for _ in range(n + 1)]
for i in range(n):
    pre[i + 1] = pre[i] + d[i]

for _ in range(m):
    a, b = map(int, input().split())
    k = bisect.bisect_left(d, b) # 二分查找
    low = max(0, n - k - a)
    upp = max(0, n - k - a + 1)

    # 1 - (a / k)
    print((low * fpow(n - k, mod - 2) * (pre[n] - pre[k] + mod) % mod + upp * fpow(n - k + 1, mod - 2) * pre[k] % mod) % mod)
相關文章
相關標籤/搜索