12.2 CW 模擬賽 賽時記錄

Yorg發表於2024-12-02

前言

\(12\) 月的第一場, 沒有大樣例

這次帶了耳塞, 注意考試方法其實並不複雜, 先看題吧

帶上耳塞, 終於舒服了

看題

\(\rm{T1}\)

結論題?

\(\rm{T2}\)

\(\rm{HS}\) 似乎講過???

但是我忘了, 一會看能不能推一下

多半是找規律

\(\rm{T3}\)

性質題?

\(\rm{T4}\)

資料結構維護吧, 只能到時候在分析


時間 : \(1 \rm{h} \ 10 \rm{min} + 1 \rm{h} \ 20 \rm{min} + 1 \rm{h}\)

策略 : 能衝前兩題就衝, 不行就用時間去後面拿分

正序開題

\(\rm{T1}\)

應該可以貪心, 掏樣例出來手玩一下看看

注意到不管對於什麼情況, 一定都是從最大的掏出來 \(k\) 個, 這樣子最優

那麼我們考慮怎麼高效的維護最值或者找到性質直接做

直接用堆做是 \(\mathcal{O}(n ^ 2 \log n)\) 的, 只能透過 \(80 \%\) 的點, 甚至因為性質不確定可能變成 \(\mathcal{O}(n ^ 2 k \log n)\) 只能拿到 \(40 \%\) 也有可能

貪心策略一定不是最優的, 一定要更深入的分析才行, 但是貪心策略的正確性就在於每次更新後重新進行 \(k\) 的取, 這不好處理

時間上來講, 這題只能打 \(80 \%\)

\(\rm{T2}\)

\(\rm{T1}\) 應該就是想複雜了, 應該不難, 所以再次大劣勢

容易想到的是, 我們可以預處理出每種逆序對數對應的全排列的數量

怎麼還是不好做呢, 一會打表找下規律

直接打出 \(20 \%\)

\(\rm{T3}\)

每個數都可以被四捨五入而來, 我們可以直接打出 \(20 \%\)

感覺像數位 \(\rm{dp}\) , 賽時沒時間研究了, 暴力打了跑路

真的可以 數位 \(\rm{dp}\) , 如果早些來一定能做的, 至少能證偽

\(\rm{T4}\)

\(f(T, k)\) 還比較好求, 具體的, 從前往後貪心, 時間複雜度 \(\mathcal{O} (\lvert T \rvert k)\)

直接比較即可, \(20 \% \sim 40 \%\)

程式碼

剩下 \(1 \rm{h} \ 30 \rm{min}\)

正打

\(\rm{T1}\)

每次對於一個堆, 我們考慮取出前 \(k\) 大的數, 記錄堆中最大值 \(max_h\) , 外面最小值 \(max_c\) , 全部減去 \(max_c - max_h + 1\) 之後把小於 \(max_h\) 的扔進去, 重複這個過程直到 $ >0 $ 的數小於 \(k\)

時間複雜度玄學, 看運氣拿分

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

int T;
int n, k;
int a[MAXN];

std::priority_queue<int> Q;
int NowChoice[MAXN];
int ChoiceNum = 0;
int Ans = 0;
void solve()
{
    /*初始化*/
    ChoiceNum = 0;
    Ans = 0;
    while (!Q.empty()) Q.pop();

    /*加入堆*/
    for (int i = 1; i <= n; i++) {
        Q.push(a[i]);
    }

    while (1)
    {
        int maxh, minc;
        bool endflag = false;
        while (!Q.empty() && ChoiceNum < k) {
            NowChoice[++ChoiceNum] = Q.top();
            if (Q.top() == 0) endflag = true;
            Q.pop();
        }
        if (endflag) {
            printf("%lld\n", Ans);
            return;
        }

        maxh = Q.top();
        minc = NowChoice[ChoiceNum];

        for (int i = 1; i <= ChoiceNum; i++) {
            NowChoice[i] -= std::min(minc, minc - maxh + 1);
        }
        Ans += std::min(minc, minc - maxh + 1);
        int NowChoiceNum = ChoiceNum;
        for (int i = NowChoiceNum; i >= 1 && (NowChoice[i] < maxh || NowChoice[i] == 0); i--) {
            Q.push(NowChoice[i]);
            ChoiceNum--;
        }
    }
}

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

    scanf("%lld", &T);
    while (T--)
    {
        scanf("%lld %lld", &n, &k);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
        }

        solve();
    }

    return 0;
}

\(\rm{T2}\)

打表玩一下, 順便最後有時間看一下能不能找到規律

#include <bits/stdc++.h>
// #define FILE_IO

int n, k;


struct node
{
    int a[20];
    
    int calc()
    {
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                if (a[j] > a[i]) ans++;
            }
        }

        return ans;
    }

    friend bool operator < (node a, node b) {
        int numa = a.calc(), numb = b.calc();

        if (numa == numb) {
            for (int i = 1; i <= n; i++) {
                if (a.a[i] > b.a[i]) return false;
                else if (a.a[i] < b.a[i]) return true;
            }
        } else {
            return numa < numb;
        }
    }
} JZY[500];

void solve()
{
    int cnt = 0;
    int a[20];
    for (int i = 1; i <= n; i++) {
        a[i] = i;
    }

    do {
        cnt++;
        for (int i = 1; i <= n; i++)
            JZY[cnt].a[i] = a[i];
    } while (std::next_permutation(a + 1, a + n + 1));

    std::sort(JZY + 1, JZY + cnt + 1);

    for (int i = 1; i <= n; i++)
        printf("%d ", JZY[k].a[i]);
}

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

    scanf("%d %d", &n, &k);

    if (n >= 20) {
        long long LSY = 1;
        for (int i = 1; i <= n; i++) LSY *= 1ll * i;
        if (LSY == n) {
            for (int i = 1; i <= n; i++) printf("%d ", n - i + 1);
        } else {
            for (int i = 1; i <= n; i++) printf("%d ", i);
        }
    } else {
        solve();
    }
    
    return 0;
}

\(\rm{T3}\)

時間不夠, 去打 \(\rm{T4}\)

\(\rm{T4}\)

總結

趨勢, 每次打不完???