2021牛客暑期多校訓練營5 個人補題記錄


比賽鏈接:Here

1001 - Away from College

1002 - Boxes

思路:只要使用一次hints,以后的每一步都可以知道剩下多少個黑球。所以最少花費是全部盒子開一遍或者先用一次hints后面再碰運氣,注意到,每開一個盒子都有一定概率直接結束(后面全都是白球或全都是黑球)

所以先排序,枚舉前綴和至倒數第二個盒子,再乘以概率 \(\frac12^{n - i}\times2\)

最后答案為:\(ans = min(sum[n],c + \sum\limits_{i = 1}^{n - 1}(sum[i] * \frac12^{n - i + 1}))\)


試一下樣例

\(n = 4,C = 0.123456\\1\ 1\ 1\ 1\)

前綴和分別是 \(1,2,3,4\)

\(ans = min(4,0,123456 + 1∗1/8+2∗1/4+3∗1/2) = min(4,2.248456)=2.248456\)

代碼
const int N = 1e5 + 10;
double a[N];
int main() {
    // cin.tie(nullptr)->sync_with_stdio(false);
    int n; double c;
    cin >> n >> c;
    double sum = 0.0, ans = 0.0;
    for (int i = 1 ; i <= n; ++i) cin >> a[i], sum += a[i];
    sort(a + 1, a + 1 + n);
    double f = 1.0;
    for (int i = n; i > 0; i -= 1) {
        ans += (1.0 - f) * a[i];
        f /= 2.0;
    }
    cout << fixed << setprecision(6) << min(sum, c + ans);
}

1003 - Cheating and Stealing

1004 - Double Strings

超級眼熟,沒想到就是 19年 CCPC秦皇島 C 題的改版。

引用一下官網題解

好的方案的構成是“一段相同的前綴+一個不同字符(a比b小)+長度相同的任意后綴”。枚舉不同的字符在兩個序列中的位置。

\(dp[i][j]\) 表示只考慮 A 中的前 i 個字符和 B 中的前 j 個字符時的相同的子序列的個數,轉移可以 \(O(1)\),這樣可以統計出相同的前綴個數,這部分是 \(O(|s|*|t|)\) 的。

長度相同的任意后綴也可以用類似的 dp 計算,或者設 A 中此時剩余長度為 x, B 中剩余長度為 y,不失一般性地設 \(x≤y\) ,現在要求的就是 \(ΣC(x,i)*C(y,i) = ΣC(x,x-i)*C(y,i) = C(x+y,x)\),這部分也是 \(O(|s|*|t|)\) 的。

using ll = long long;
#define mod 1000000007
ll la, lb, adf[5010], adg[5010], f[5010], g[5010];
char a[5010], b[5010];

int main() {
    cin >> a + 1 >> b + 1;
    la = strlen(a + 1);
    lb = strlen(b + 1);
    for (int i = 1; i <= la; i++) {
        for (int j = 1; j <= lb; j++) adf[j] = adg[j] = 0;
        for (int j = 1; j <= lb; j++) {
            if (a[i] == b[j]) adg[j] = g[j - 1] + 1;
            adf[j] = f[j - 1];
            if (a[i] < b[j]) adf[j] += g[j - 1] + 1;
        }
        for (int j = 1; j <= lb; j++) {
            adg[j] += adg[j - 1];
            adf[j] += adf[j - 1];
            g[j] += adg[j];
            f[j] += adf[j];
            adg[j] %= mod; adf[j] %= mod;
            g[j] %= mod; f[j] %= mod;
        }
    }
    cout << (f[lb] % mod + mod) % mod;
    return 0;
}

1005 - Eert Esiwtib

1006 - Finding Points

1007 - Greater Integer, Better LCM

1008 - Holding Two (簽到)

題目描述

在$n \times m$ 的 $01$ 矩陣中
請問是否存在任意3個點 $(-1\le x_1 - x_2 = x_2 - x_3 \le 1,-1\le y_1 - y_2 = y_2 - y_3 \le 1)$
如果存在則輸出任何一種情況,否則輸出 -1

簡單來說就是滿足在橫行,豎行,斜行中任意的連續的3個的元素不能都為0,或都為1;

考慮了挺多情況發現下面這種寫法對於任何 \(n,m\) 都成立

\[1100..\\ 0011..\\ 1100..\\ .. \]

【AC Code】

int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n, m; cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j)
            cout << ((i & 1) ? ((j + 1) / 2 % 2) : !((j + 1) / 2 % 2));
        cout << "\n";
    }
}

1009 - Interval Queries

看不懂,沒思路,fw二連

1010 - Jewels

題目描述

把大海想象成一個三維坐標,我們的坐標為 $(0,0,0)$,現在有 $n$ 個寶石,但是每一秒寶石會以 $v_i$ 下沉,用 $d$ 來表示強度: 該時刻打撈的寶石與你之間的距離。請最小化總強度。


開賽的時候看了下這個似乎是貪心排序就能寫?自信交了一發,WA打臉。之后隊友看出 \(k\) 才是簽到,所以就比賽時沒管這道題了

正解的思路:所有的寶石肯定都在 \(0\) ~ \(n-1\)\(n\) 個時刻被挖掉,那么問題就變成一個最小權匹配了,一邊是時刻,一邊是寶石,邊權就是在這個時刻挖這個寶石所消耗的體力值。

稍微套一下板子就可以了

代碼
ll n, ans;
struct jew {ll z, v, id;} a[310];
ll f(ll t, jew x) {t--; return t * t * x.v * x.v + 2 * t * x.v * x.z;} //邊權
void g(int x) {
    int t;
    for (int i = 1; i <= n; i++)  //找到當前寶石所匹配的時間
        if (a[i].id == x) {t = i; break;}
    for (int i = 1; i <= n; i++) { //枚舉所有的邊
        if (f(i, a[i]) + f(t, a[t]) > f(t, a[i]) + f(i, a[t])) { //找增廣路
            swap(a[i], a[t]); //匹配寶石到時間更優的位置
            g(a[t].id); g(x); //暴力遞歸找增廣路
        }
    }
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        ll x, y, z, v;  cin >> x >> y >> z >> v;
        ans += x * x + y * y + z * z;
        a[i] = (jew) {z, v, i};
    }
    for (int i = 1; i <= n; i++) g(i);
    for (int i = 1; i <= n; i++) ans += f(i, a[i]); //累加答案
    cout << ans << "\n";
    return 0;
}

1011 - King of Range

題目描述

長度 $n$ 的序列,定義區間大小為區間內最大值減去最小值的值,請問在 $m$ 次查詢中有多少對區間嚴格大於 $k$


\(R_i\)\(l=i\) 時,滿足極差大於 \(k\) 的最小的 \(r\),如果不存在則記 \(R_i = n+1\)。那么顯然有 \(R_1 ≤ R_2 ≤ ... ≤ R_n\)

因為區間端點是單調的,明顯的單調隊列問題

其中一個遞增序列,隊首維護最小值,一個遞減序列,隊首維護最大值,每次彈出兩個隊列中隊首靠前的一個,直到極差 \(≤ k\)。那么就可以在均攤 \(O(1)\) 的時間內求 \(R_i\) 了。

不過需要同時維護兩個(一個求最大值,另一個求最小值)

代碼
const int N = 1e5 + 10;
int a[N], q1[N], q2[N], h1, t1, h2, t2;
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    while (q--) {
        int k;
        cin >> k;
        h1 = h2 = t1 = t2 = 1;
        q1[1] = q2[1] = 1;
        int j = 1;
        ll ans = 0;
        for (int i = 1; i <= n; ++i) {
            if (q1[h1] < i) ++h1;
            if (q2[h2] < i) ++h2;
            while (j < i || a[q2[h2]] - a[q1[h1]] <= k) {
                if (j == n)break;
                ++j;
                while (t1 >= h1 && a[q1[t1]] > a[j])--t1;
                while (t2 >= h2 && a[q2[t2]] < a[j])--t2;
                q1[++t1] = j;
                q2[++t2] = j;
            }
            if (a[q2[h2]] - a[q1[h1]] <= k) break;
            ans += n - j + 1;
        }
        cout << ans << "\n";
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM