12.10 CW 模擬賽 賽時記錄

Yorg發表於2024-12-10

前言

最近發現只要每分鐘都在做有意義的事就不算頹, 同理的, 這場考試只要每分鐘都在想些事情, 也就不算

短期的主要目標就是利用好時間, 其他的問題我基本上已經解決了, 就是時間分配利用上的問題
所以就只抓時間分配, 這段時間先不去想別的, 就好好把時間利用起來, 不死磕, 不畏難, 利用好時間, 分配好

立個 \(\rm{flag}\) , 這周不刷 \(\rm{B}\) 站了

看題

粗看了一遍, 都不太好做, 好像給了好幾道計數

\(1 \rm{h} + 30 \rm{min} + 30 \rm{min} + 30 \rm{min} + 1\rm{h} 10 \rm{min}\)

\(\rm{T1}\)

思路

轉化題意, 要求把一個字串拆分成若干段, 要求相鄰子串至少有一個是倍數串

考慮 \(\rm{dp}\) 來計數

樸素的想法是 \(f_{i, 0 / 1}\) 表示切分到了 \(i\) 位置, 其中最後一段是 / 不是倍數串的切分方案數

顯然有

\[f_{i, 0} = \sum_{S_{j + 1 \sim i} \ 不是倍數串} f_{j, 1} \]

\[f_{i, 1} = \sum_{S_{j + 1 \sim i} \ 是倍數串} f_{j, 0} \]

這個轉移是 \(\mathcal{O} (n ^ 2)\) 級別的, 只能透過 \(\rm{Subtask} \ 1\)
加上預處理每個點之前的

那麼怎麼去最佳化時間複雜度呢?

僅僅找到每一個點對應的倍數串都已經 \(n^2\) 了, 確實不好實現

考慮 \(\rm{Subtask}\) 的打法, 居然不會???

判斷是否能夠成為倍數串好像還需要類似於高精度的方法, 毒瘤啊

預期得分 : \(30 \rm{pts}\)

實現

框架

首先讀入, 然後直接計算符合要求的倍數串, 刷表去做

關於怎麼計算符合要求的倍數串:

  1. 列舉開頭
  2. 從前往後, 先加上這個數再對 \(D\) 取餘, 如果變成 \(0\) 就標記倍數串

程式碼

稍微改了一下實現, 大樣例都不想測

#include <bits/stdc++.h>
#define int long long
// #define FILE_IO
const int MAXN = 1020;

int T;
int D; std::string S; int n;

class Sol_Class
{
private:

public:
    bool IsMul[MAXN][MAXN];
    /*計算每個點為開頭的倍數串*/
    void init()
    {
        for (int i = 1; i <= n; i++) { // 列舉起點
            int nownum = 0;
            for (int j = i; j <= n; j++) {
                nownum *= 10; nownum += S[j] - '0';
                nownum %= D;
                if (nownum) IsMul[i][j] = false; else IsMul[i][j] = true;
            }
        }
    }

    int f[MAXN][2];
    /*計算*/
    void solve()
    {
        memset(f, 0, sizeof(f));
        for (int i = 1; i <= n; i++) {
            f[i][0] = !IsMul[1][i], f[i][1] = IsMul[1][i];
            for (int j = 1; j < i; j++) {
                if (IsMul[j + 1][i]) f[i][1] += f[j][0] + f[j][1];
                else f[i][0] += f[j][1];
            }
        }

        printf("%lld\n", f[n][1] + f[n][0]);
    }
} Sol;

signed main()
{
#ifdef FILE_IO
    freopen("digit.in", "r", stdin);
    freopen("digit.out", "w", stdout);
#endif

    scanf("%lld", &T);
    while (T--) {
        std::cin >> S; scanf("%lld", &D); n = S.length(); S = ' ' + S;
        Sol.init();
        Sol.solve();
    }
}

\(\rm{T2}\)

思路

\(\rm{T1}\) 確實不會做, 來開新題

很快就可以注意到, 之所以獨特數的數量有限, 是因為對於 \(B\) 進位制, 最多隻能出現 \(B\) 種不同的數, 我們考慮直接排列組合這 \(B\) 種不同的數, 轉化判斷一下就可以得出答案, 這下可以有 \(50 \rm{pts}\) , 非常的賺

容易發現對於某些特殊情況, 從大到小列舉似乎可以騙到更多的分

期望得分 : \(50 \sim 70 \rm{pts}\)

實現

框架

直接 \(\rm{dfs}\)

程式碼

\(\rm{T3}\)

思路

前面兩題連思路都沒有也是抽象, 繼續打吧

轉化題意, 在環上相鄰點之間可以選擇是否建邊, 要求建邊之後, 對於 \(P\) 個點對聯通
求最小的花費之和

考慮一個 \(\mathcal{O} (nP)\) 的演算法可以透過 \(40 \%\)

使用傳統 \(\rm{trick}\) , 列舉不使用一條邊 \(\mathcal{O} (n)\) , 然後在這種情況下做操作 \(\mathcal{O} (P)\) , 解決

期望得分 : \(40 \rm{pts}\)

\(\rm{T4}\)

思路

我覺得這種策略挺好的, 有節目效果

考慮設計一個 \(\mathcal{O} (nm)\) 級別的演算法透過 \(40 \%\)

怎麼看不懂樣例啊哥們???

真的看不懂啊???

算了直接暴力模擬即可, 每次乘起來, 但是怎麼過不了樣例???

那這題做不了, 擺了