Codeforces Round #674 (Div. 3)

Codeforces Round #674 (Div. 3)

A. Floor Number

題目大意

題目大意爲:一個學生公寓中有多個房間,每一個房間有門牌號。已知一樓有且僅有兩個房間,其門牌號分別爲 1 和 2。給定數字 \(x\) ,以後每層樓中有 \(x\) 間房間。問:給你門牌號 \(n\) 和數字 \(x\) 。給出所在樓層。c++

1 <= n, x <= 1000數組

*800ide

解題思路

很是簡單,分狀況討論:this

  • 若是 $n \leq 2 $ ,則返回 \(1\)
  • 不然,\(n \leftarrow n - 2, ans = \lceil\dfrac{n}{x}\rceil\)

代碼

void solve(){
    int n = read(), x = read();
    if (n <= 2) cout << 1 << '\n';
    else cout << (n - 2 + x - 1)  / x + 1 << '\n';
}

B. Symmetric Matrix

題目大意

\(n\)\(2 \times 2\) 的正方形嘗試拼接出一個 \(m\times m\) 的關於主對角線對稱矩陣。spa

若是能夠輸出:YES 不然輸出:NOcode

*900blog

解題思路

題目看起來有點麻煩,閱讀起來也比較長,實際上仔細一想,只要邊長能 mod 2,則咱們運用貪心的思路考慮問題,只須要一個 \(2\times 2\) 的主對角線對稱矩陣便可。繼承

主要的思路方向是大小類比,轉大爲小。遊戲

代碼

void solve(){
    int n = read(), m = read();
    int have = 0;
    for (int i = 0; i < n; ++ i){
        int ul = read(), ur = read();
        int dl = read(), dr = read();
        if (dl == ur) have = 1;
    }
    if (m % 2 != 0 || have == 0) wprint("NO");
    else wprint("YES");
}

C. Increase and Copy

題目大意

給你一個數組 \(a\) ,最開始只包含一個數字 \(1\) 。你能夠作以下兩種操做:ci

  1. \(a\) 中的某一元素加一
  2. 複製 \(a\) 中某一元素到數組末尾

回答,須要最少操做多少步,能使數組 \(a\) 中的總和大於 \(n\)

1 <= n <= 1e9

*1100

思路分析

通過思考,顯然倍增的效率是大於自增的,因而咱們能夠初步得出結論,咱們須要先自增而後再倍增

設咱們自增到 \(k\) 再進行倍增。則須要的步驟數爲:

\[ans = \lceil \dfrac{n - k}{k}\rceil + k - 1 \]

處理一下變爲:

\[ans = \lceil \dfrac{n}{k} \rceil + k - 2 \]

由基本不等式可得:當 \(k = \sqrt{n}\) 時存在最小值,又由於這裏皆爲整數,經過計算機驗證以後獲得以下結論:

\[\begin{aligned} 對於: &\left\{ \lceil \dfrac {n}{k} \rceil + k\right\} or \left\{ \lfloor \dfrac {n}{k} \rfloor + k\right\}\qquad &k \in Z \\ \quad \\ &k = \lfloor \sqrt n \rfloor \lor k = \lceil \sqrt n\rceil 時存在最小值 \end{aligned} \]

能夠記住如上結論。

代碼

void solve(){
    int n = read();
    int ans(0);
    int x = sqrt(n);
    ans = (n + x - 1) / x + x - 2;
    cout << ans << '\n';
}

D. Non-zero Segments

題目大意

給定一個長度爲 \(n\) 且不含 \(0\) 的序列 \(a\),能夠插入任何數字(能夠爲 \(\infty\) )。請問最少插入多少個數字才能保證序列中任意連續子序列和不爲 \(0\)

  • 2 <= n <= 200000
  • -1e9 <= ai <= 1e9

*1500

思路分析

這題關鍵點在於 快速肯定區間和爲 0 的狀況

由於作過相似的,因此能夠很快的寫出來:即用 hashmap

接下來複制我在CF回覆別人的部分:

Let us define \(presum_{k}\) as the prefix sum, \(a_k\) as the array.

\[presum_{k} = \sum_{i=1}^{k} a_i \]

Let's define \(i,j\) as indexs(\(i < j\)):

  • if \(presum_{i} == presum_{j}\), we can know that

\[presum_{j} - presum_{i} == 0 \rightarrow sum\{a_{i+1}, \cdots, a_{j}\} == 0 \]

In order to avoid this situation, we insert a \(+\infty\) behind the \(a_j\). Like this: \(a_{i+1}, \cdots, a_{j-1}, \stackrel{\infty}{\downarrow},a_{j}\).Then reconsider from \(a_j\)

代碼

void solve(){
    int n = read();
    unordered_map<LL, int> mp; // 0 --> not in, 1 --> in
    mp.clear();
    LL sum(0), ans(0);
    for (int i = 0; i < n; ++ i){
        int f = read();
        sum += f;
        if (sum == 0 || mp.count(sum)) { 
            ++ ans; 
            mp.clear();  // Simulate inserting an infinite number
            sum = f; 
            mp[sum] = 1;
        }
        else mp[sum] = 1;
    }
    cout << ans << '\n';
}

E. Rock, Paper, Scissors

題目大意

Alice和Bob進行剪刀石頭布的遊戲,總共進行\(n\)局。

Alice出石頭\(a_1\)次,出剪刀\(a_2\)次,出布\(a_3\)次。

Bob出石頭\(b_1\)次,出剪刀\(b_2\)次,出布\(b_3\)次。

問Alice最多贏多少次,最少贏多少次。

*1800

思路分析

最大值很是簡單,咱們但願儘量能贏

實際上這題應該有更加數學的思路,可是考慮到整個問題空間的大小,所以能夠經過暴力搜索直接解決。

面對樣本空間很小的題目,咱們能夠優先計算窮舉的複雜度,快速解決。

時間複雜度大體爲 \(\mathcal{O}(2\cdot 3!)\)

代碼

int a[5], b[5];
int mn;
void dfs(int ca, int cb, vector<int> visa, vector<int> visb, int cnt, vector<int> fa, vector<int> fb){
    visa[ca] = visb[cb] = 1;
    
    if (fa[ca] >= fb[cb]){
        if ((ca + 1) % 3 == cb) cnt += fb[cb];
        fa[ca] -= fb[cb];
        int ok =0;
        for (int i = 0; i < 3; ++ i){
            if (visb[i] == 0) { // 誰沒有了,就在他可選的中間挑選一個
                dfs(ca, i, visa, visb, cnt, fa, fb);
                ok = 1;
            }
        }
        if (ok == 0){ // 沒有什麼能夠選的了,說明結束了
            mn = min(mn, cnt);
            return;
        }
    }
    else {
        if ((ca + 1) % 3 == cb) cnt += fb[cb];
        fb[cb] -= fa[ca];
        int ok = 0;
        for (int i = 0; i < 3; ++ i){
            if (visa[i] == 0) {
                dfs(i, cb, visa, visb, cnt, fa, fb);
                ok = 1;
            }
        }
        if (ok == 0){
            mn = min(mn, cnt);
            return;
        }
    }
}


void solve(){
    int n = read();
    for (int i = 0; i < 3; ++ i) a[i] = read();
    for (int i = 0; i < 3; ++ i) b[i] = read();

    int mx(0);
    for (int i = 0; i < 3; ++ i) mx += min(a[i], b[(i + 1) % 3]);

    VI visa(3, 0), visb(3,0); // visa --> a是否訪問, visb --> b是否訪問
    VI fa(3, 0), fb(3, 0); // fa --> a 的各個數個數, fb --> 同 a
    mn = inf;
    for (int i = 0; i < 3; ++ i) fa[i] = a[i], fb[i] = b[i];
    for (int i = 0; i < 3; ++ i){ // 枚舉各類狀況
        for (int j = 0; j < 3; ++ j) dfs(i, j, visa, visb, 0, fa, fb);
    }
    wprint(mn, mx);
}

F. Number of Subsequences

題目大意

給定一個長爲 \(n\) 含有abc?的字符串,?多是abc中的任意一個,求全部可能的無?字符串中,子序列abc出現的次數.結果須要 mod \(1e9 + 7\)

3 ≤ n ≤ 200000

*2000 dp

思路分析

經過數據大小和題目所問的,咱們能夠很快得出大體思路:時間複雜度爲 \(\mathcal{O}(n)\) 的線性dp

假如是 dp ,咱們確定須要記錄一些東西才能保證在 \(\mathcal{O(1)}\) 的時間內進行狀態轉移,而且須要考慮記錄什麼和 ? 如何進行處理。

因爲咱們只須要考慮 abc ,所以咱們能夠只記錄:

  1. a的數量 \(f[0]\)
  2. ab 的數量 \(f[1]\)
  3. abc 的數量 \(f[2]\)

當沒有遇到 ? 時轉移方程很是簡單,這裏就不作贅述。

下面考慮當須要疑問號的狀況便可:

首先,咱們清楚疑問號能夠變爲 a,b,c 任意一種的,用生物學的知識去想有點像分裂的過程:

當碰到 ? 號時,會在原有的基礎上出現 \(3\) 種新狀況。

  1. 對於 \(cur_1\) 。其 \(f[0] = f[0] + 1\)
  2. 對於 \(cur_2\)。 其 \(f[1] = f[1] + f[0]\)
  3. 對於 \(cur_3\)。 其 \(f[2] = f[2] + f[1]\)

而他們各自又繼承了 cur的全部值。所以:

\[ \begin{aligned} &for\; new\;cur:\\ &f[2] = 3*f[2] + f[1] \\ &f[1] = 3*f[1] + f[0] \\ &f[0] = 3*f[0] + 1 \\ \end{aligned} \]

這對了嗎?,實際上這樣寫表明你沒有理解其核心。須要知道的最開始只擁有 \(1\) 種狀況,但每遇到一個 ? 會分裂出 \(3\) 種狀況,不妨假設 ? 的個數爲 \(c_q\) 。則當前存在 \(3^{c_q}\) 種狀況,因而你每次增長都須要考慮全部的狀況。而上述方程中:a -> abab -> abc 的轉移自帶魯棒性已經考慮了這個問題,而 null -> a 的過程則沒有考慮。

所以,咱們須要記錄當前空間中存在狀況的總個數 base ,當遇到 ? 時更新 base 的值。

作這種 \(\mathcal{O}(1)\) 的計數 dp ,確定是須要記錄某些數據,若須要計數的爲一個具備順序的序列,不妨前綴考慮每一個子段的個數,逐個轉移。

代碼

const int maxn = 5;
LL f[maxn];
void solve(){
    int n = read();
    mst(f, 0);
    string ss;
    cin >> ss;
    LL base = 1;
    for (int i = 1; i <= n; ++ i){
        char ch = ss[i - 1];
        if (ch == 'a'){
            f[0] = (f[0] + base) % MOD;
        }else if (ch == 'b'){
            f[1] = (f[1] + f[0]) % MOD;
        }else if (ch == 'c'){
            f[2] = (f[2] + f[1]) % MOD;
        }else {
            f[2] = (3 * f[2] % MOD + f[1]) % MOD;
            f[1] = (3 * f[1] % MOD + f[0]) % MOD;
            f[0] = (3 * f[0] % MOD + base) % MOD;
            base = (1LL * base * 3) % MOD;
        }
        // print(f, 3);
    }
    cout << f[2] << '\n';
}

UPD:
小號變大號太真實了:

相關文章
相關標籤/搜索