「比賽記錄」CF Round 954 (Div. 3)

_yuen發表於2024-07-19

Codeforces Round 954 (Div. 3)

題目列表:

  • A. X Axis
  • B. Matrix Stabilization
  • C. Update Queries
  • D. Mathematical Problem
  • E. Beautiful Array
  • F. Non-academic Problem
  • G1. Permutation Problem (Simple Version)
  • G2. Permutation Problem (Hard Version)


A. X Axis

題目大意:

t組測試樣例,每組樣例給出三個整數,分別表示數軸上三個點的座標,選取數軸上任意一個點,使得這個點到給定的三個點的距離和最小,求出這個最小距離和。

分析:

純水題,顯然答案就是給定的這三個點中最大的座標減去最小的座標。

B.Matrix Stabilization

題目大意:

C. Update Queries

題目大意:

給你兩個字串S、C,長度分別為n和m,以及一個大小為m的陣列$ind[]$表示索引,有m次操作,從1到m依次進行,第i次操作需要把 $ S_{ind[i]} $變為$C_i$。並且你可以把字串$C$以任意順序排列,比如對於$C=abc$,你可以將它變為$acb,bca,bac,cab,cba$。求出m次操作之後所能得到的字典序下最小的$S$。

D.Mathematical Problem

分析:

\(n\le20\) 這是 \(O(n^2)\) ?NO!

非 DP 整體複雜度 \(O(n)\) 可做!可以當成並不困難的規律題做。

思路分析:

首先我們要想到一些特殊情況(原因在程式碼註釋中解釋):

  1. \(n=2\) 時直接輸出原數;
  2. \(n=3\) 時,若第一位或第三位數為 0,則答案為 0;
  3. \(n>3\) 時,有 0 則答案為 0。

此題有一個顯而易見的性質:對於已知的一組數,除了 0 和 1 之外的數直接加和答案最小。

證明:顯然。其他題解中對於這一條已有很好的解釋,這裡不再多做敘述了,也可以自己手膜一下容易發現。

在想到這條性質後,那麼我們需要做的就是分出一個二位數使答案最優,以下我們稱要找的這個二位數為最優二位數

按照容易想到的 \(O(n^2)\) 做法,就是遍歷出所有二位數,對每一個二位數,都求一遍 1 以為的數的加和,答案即為所有和的最小值。

那麼我們能不能直接 \(O(n)\) 找出最優二位數呢,再仔細分析一下,又發現以下性質:

(若兩個二位數 \(a,b\),有 \(a\) 優於 \(b\),則我們寫成 \(a>b\))於是有如下“優排列”(注意 11,21 等個位數為 1 的數的位置)

$ 12=13=14=15=16=17=18=19>11=22=...=29>21>...$
以此類推。(不含個位數為 0 的, 因為特殊情況中 0 已經考慮)即十位數相同的所有二位數,個位數不為 1 的數要優於個位數為 1 的。

為什麼呢?我們假定將原數列除 1 之外的總和為 \(sum\),找出最優二位數之後除 1 之外的總和為 \(you\),最優二位數為 12 時,則 \(you\) 相比於 \(sum\) 增加了 \(12-2=10\),(\(sum\) 中只加了 2 ,而 \(you\) 中加了 12,對於所有十位數為 1 的二位數,\(you-sum\) 的值都為 10,而對於 11 ,\(you-sum=11-0=11\) (因為 1 在 \(sum\) 都不進行計算)。\(11>10\),所以十位數相同時,個位數不為 1 的要優於個位數為 1 的。那麼十位數為 2、個位數不為 1 時,\(you-sum=18\),對於 21,\(you-sum=20\),同理,便得出了上述“優排列”。

這樣我們就可以 \(O(n)\) 找出最優二位數了,之後直接計算即可。

處理的時候我們可以分別找出最小的各位為 1 的二位數和各位不為 1 的二位數,比較它們十位數即可。

程式碼如下:

#include<bits/stdc++.h>
using namespace std;
 
const int N = 25;
 
int t, n, a[N];
 
int main(){
    // freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
    
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n); bool if_end = false;
        for(int i=1; i<=n; i++){
            scanf("%1d", &a[i]);
        }
 
        if(n == 2){ //n-2=0,即不需要新增運算子,直接輸出
            int ans = 0;
            ans = a[1] * 10 + a[2];
            printf("%d\n", ans);
            continue;
        }
        if(n == 3){
            if(a[1] == 0 or a[3] == 0){//特判0
                puts("0");
                continue;
            }
            if(a[2] == 0){//n=3,只能有一個運算子,第二位數為0答案不為0
                if(a[1] == 1) printf("%d\n", a[3]);
                else if(a[3] == 1) printf("%d\n", a[1]);
                else printf("%d\n", a[1]+a[3]);
                continue;
            }
        }
 
        int sm = 2008, id = 0;//記個位數不為1的最小二位數及位置
        int _1 = 2008, id_1 = 0;//個位數為1的最小二位數及位置
        for(int i=1; i<=n; i++){
            if(n > 3 and a[i] == 0){ //有0,全部相乘便為0
                puts("0"); if_end = true; break;
            }

            int now = a[i-1] * 10 + a[i];
            if(i > 1 and a[i] == 1){
                if(_1 > now) _1 = now, id_1 = i;
            }
            if(i > 1 and a[i] != 1){
                if(sm > now) id = i, sm = now;
            }
        }
        if(if_end) continue;
 
        int y = sm - sm % 10;//y為個位數不為1的最小二位數的十位數
        if(_1 < y and _1) sm = _1, id = id_1;//此時最優二位數為最小的個位數為1的二位數
 
        int ans = sm;
        for(int i=1; i<=n; i++){ //1之外的加和
            if(i == id or i == id - 1 or a[i] == 1) continue;
            ans += a[i];
        }
 
        printf("%d\n", ans);
 
    }
 
    return 0;
}

相關文章