首页 > 其他分享 >CW 12.26 模拟赛 赛时记录

CW 12.26 模拟赛 赛时记录

时间:2024-12-26 14:59:01浏览次数:3  
标签:mathbb 赛时 int Ans 12.26 mathcal rm CW dp

前言

虽然说有点难受, 但是还是好好考

考试只需要管考试相关的即可, 别想太多

冷静, 就这样

看题

先过一遍吧, 看看感觉怎么样, 今天时间要短一点, 不开心

\(\rm{T1}\)

至少题意清楚, 不管了

\(\rm{T2}\)

这么有实力, 很像 \(\rm{Indec \ Sequence}\)

\(\rm{T3}\)

多半要观察性质

\(\rm{T4}\)

这个打个模拟分就行了

\(\rm{T1}\)

思路

冷静, 好好看

用数学语言转化一下,
对于长为 \(n\) 的数列 \(b\) , 从中选出大小为 \(k (1 \leq k \leq n)\) 的非空可重子集 \(b\)
求 \(\displaystyle \max_b \sum_{i = 1}^{\lvert b \rvert} \left[ b_i > \frac{\sum_{j = 1}^{\lvert b \rvert} b_j}{\lvert b \rvert} \right]\)

玩一下样例 \(2\) 看下性质

好像一边从小往大选, 一遍从大往小选很对啊

打个 \(\mathcal{O} (n^2)\) 看下对不对, 再想优化, 要是不对只能寄

怎么和大样例差了 \(2\) , 难崩

那怎么办, 容易想到一些构造方法把刚刚的做法卡掉, 难受

贪心不行考虑 \(\rm{dp}\)

考虑两个 \(\rm{dp}\) 合并答案

令 \(l_{i, j}\) 表示序列中最大值为 \(j\) , 选了 \(i\) 个数时最大的和, \(r_{i, j}\) 表示序列中最小值为 \(j\) , 选了 \(i\) 个数时最小的和

哪些状态是可以合并计算答案的, 显然的, 如果对于 \(r_{i, j}\) 可以找到 \(l_{k, j}\) 满足

\[\frac{r_{i, j} + l_{k, j}}{i + k} < j \]

那么其对答案的贡献就是 \(i\)

又感觉很对啊, 但是还是先打个 \(\mathcal{O} (n^2)\) 出来检查一下

然后发现 \(l, r\) 都不好转移, 但是你发现好像可以直接 \(\mathcal{O} (n^2)\) 算

反正现在也最多达到暴力分, 想想这个不亏

形式化一下就是枚举 \(j\) 然后计算贡献, 和之前的贪心很像, 只是换了枚举方向

现在我们的做法是, 枚举分割点 \(p\) , 然后对于 \(p\) 右侧我们从小到大找 \(i\) 个数字, 其和为 \(sum\) , 然后在 \(p\) 左侧从小到大找 \(j\) 个数字看是否符合要求, 如果用 \(Pre\) 表示前缀和, 即检查 \(\displaystyle\frac{Pre_j + sum}{i + j} < a_p\)

这个是 \(\mathcal{O} (n^3)\) 的, 考虑优化

直觉上是有单调性的, 但是仔细分析了一下又不存在

看起来没时间了, 先把暴力打了再说

实现

\(\mathcal{O} (n^3)\) 算法

框架

按照上面说的模拟即可

代码

class Brute_Force
{
private:
    int Ans = 0;

public:
    void solve() {
        std::sort(a + 1, a + n + 1);

        for (int p = n; p >= 1; p--) {
            int rsum = 0;
            for (int r = p; r <= n; r++) {
                rsum += a[r];
                int lsum = 0; bool flag = false;
                for (int l = 1; l < p; l++) {
                    lsum += a[l];
                    if ((r - p + 1ll + l) * a[p] > lsum + rsum) {
                        Ans = std::max(Ans, r - p + 1ll);
                    }
                }
            }
        }
        printf("%lld", Ans);
    }
} BF;

\(\rm{T2}\)

思路

这个题和之前做过的一题很像, 说不定可以冲正解?

唯一的区别就是差 , 而且每次可以 \(\pm k\)
分数组第一位一定要是 \(0\)
你发现在序列之中匹配完了之后, 剩下 \(k\) 个数就要做 \(k\) 次操作

如果匹配了 \(i\) 对, 剩下来 \(k\) 个数, 总花费为 \(k + i\)

你发现时间复杂度给的可以放过 \(\mathcal{O} (n 2^n)\) , 往这个方向想

你又发现一次操作至少可以消掉一个数, 那么肯定是尽可能匹配优先

用 \(\rm{dp}\) 的方法做, 差不多会了, 一会继续推, 实在太难受了先打暴力


打了 \(70 \rm{pts}\) 的暴力, 过来全力冲这个题

首先规定负数为绝对值小的那一方, 方便处理

令 \(dp_{i, \mathbb{S}}\) 表示考虑到第 \(i\) 个负数, 当前余下的正数集合为 \(\mathbb{S}\) , 当前的匹配花费最少是多少加上余下了多少个负数 (其实就是最后的花费)

这个地方值得注意的是如果一个数没法全匹配, 那可以直接跳过把这个都放到最后来计算贡献
还有一个地方是你需要预处理每个负数可以被哪些正数拼起来消掉, 方便处理 \(\rm{dp}\) 转移中的 \(\mathbb{S} ^ {\prime}\)

\(\rm{dp}\) 柿子差不多长这样:

\[\begin{cases} \begin{aligned} & dp_{i, \mathbb{S}} \stackrel{min}{\longleftarrow} dp_{i - 1, \mathbb{S}} + 1 \\ & dp_{i, \mathbb{S}} \stackrel{min}{\longleftarrow} dp_{i - 1, \mathbb{S} ^ {\prime}} + cost \end{aligned} \end{cases} \]

总时间复杂度 \(\mathcal{O} (n^2 2^n) - \mathcal{O} (n 2^n)\)

其实实际上要比预计的复杂度更高, 但总体上差不多这样, 能打出来就谢天谢地了, 还是别管挂分了

然后挂了, 进一步发现还是假的, 哈哈哈哈哈

实现

框架

首先把所有数据处理成符合上文正负数的性质

然后预处理每个负数可以被哪些数拼起来

然后转移 \(\rm{dp}\) 方程, 滚一下降成双维

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 25;
const int MAXVAL = (1 << 23);

int n;
int a[MAXN], dif[MAXN];

std::vector<int> pos, neg;
std::vector<int> divide[MAXN];
/*将所有数据预处理好*/
void init() {
    /*预处理正负数*/
    int suma = 0, sumb = 0; // 表示正负数的绝对值之和
    for (int i = 1; i <= n; i++) {
        if (dif[i] < 0) sumb += -dif[i];
        if (dif[i] > 0) suma += dif[i];
    }

    if (suma > sumb) {
        for (int i = 1; i <= n; i++) {
            if (dif[i] < 0) neg.push_back(-dif[i]);
            if (dif[i] > 0) pos.push_back(dif[i]);
        }
    } else {
        for (int i = 1; i <= n; i++) {
            if (dif[i] > 0) neg.push_back(dif[i]);
            if (dif[i] < 0) pos.push_back(-dif[i]);
        }
    }

    /*预处理每个负数可以被那些数拼起来*/
    for (int i = 0; i < neg.size(); i++) {
        for (int S = 0; S < (1 << pos.size()); S++) {
            int cnt = 0, nowS = S, sum = 0;
            while (nowS) {
                if (nowS & 1) sum += pos[cnt];
                cnt++, nowS >>= 1;
            }
            if (sum == neg[i]) divide[i].push_back(S);
        }
    }
}

int dp[2][MAXVAL];
/*处理 dp*/
void solve() {
    int now = 0, nxt = 1;
    /*初始化*/
    memset(dp, 0x3f, sizeof dp);
    dp[now][(1 << pos.size()) - 1] = 0;

    /*递推*/
    for (int i = 0; i < neg.size(); i++) {
        std::swap(now, nxt); memset(dp[now], 0x3f, sizeof dp[now]);
        for (int S = 0; S < (1 << pos.size()); S++) {
            dp[now][S] = std::min(dp[now][S], dp[nxt][S] + 1);
            for (int j = 0; j < divide[i].size(); j++) {
                int Splus = divide[i][j];
                if (S & Splus) continue;

                int Sp = S | Splus;
                dp[now][S] = std::min(dp[now][S], dp[nxt][Sp] + std::__popcount(Splus));
            }
        }
    }

    /*计算答案*/
    int Ans = 0x3f3f3f3f3f3f3f3f3f;
    for (int S = 0; S < (1 << pos.size()); S++) {
        Ans = std::min(Ans, dp[now][S] + std::__popcount(S));
    }
    printf("%lld", Ans);

}

signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); dif[i] = a[i] - a[i - 1]; }

    init();

    solve();

    return 0;
}

\(\rm{T3}\), \(\rm{T4}\)

时间肯定不够了, 把模拟分打完就没有了

状态太差了, 现在想题需要分精力出来控制呼吸 \(\cdots\)

#include <bits/stdc++.h>
const int MAXN = 10;

int n;
int aimc[MAXN];
int Ans = 0;

int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++) scanf("%d", &aimc[i]);

    int now[MAXN], p[MAXN]; for (int i = 1; i <= n; i++) p[i] = i;

    do {
        for (int i = 1; i <= n; i++) now[i] = p[i];
        int c[MAXN];
        for (int i = 1; i < n; i++) {
            c[i] = 0;
            for (int j = i + 1; j <= n; j++)
                if (now[i] > now[j])
                {
                    std::swap(now[i], now[j]);
                    c[i]++;
                }
        }

        bool same = true;
        for (int i = 1; i < n; i++) {
            if (c[i] != aimc[i]) { same = false; break; } }

        if (same) Ans++;

    } while (std::next_permutation(p + 1, p + n + 1));

    printf("%d", Ans);

    return 0;
}

\(\rm{T4}\) 没时间了

总结

方法是好的, 下午好好补题

标签:mathbb,赛时,int,Ans,12.26,mathcal,rm,CW,dp
From: https://www.cnblogs.com/YzaCsp/p/18632854

相关文章

  • 2024.12.26 os lab3
    2024.12.26oslab3原代码地址:https://github.com/BUPT-OS/easy_lab/tree/lab3运行未修改的代码,并且注释掉cout时发生错误:malloc():corruptedtopsize如果不注释cout,可以正常运行1.不注释cout时堆内存的详细分析1.程序启动阶段在程序启动时,堆的初始状态为空,堆顶指......
  • 如何使用WGAN-GP生成一维滚动轴承振动数据样本。以西储大学(CWRU)数据集为例,提供一个基
    使用WGAN-GP生成一维滚动轴承振动数据样本。以西储大学(CWRU)数据集为例,提供一个基于训练好的权重参数文件进行测试的代码。WGAN-GP-1D轴承振动数据样本生成方法,西储大学数据集为例,可替换自己的数据。代码注释清楚,包含训练过程的代码train_gan和基于训练好的权重参数文件......
  • 12.24 CW 模拟赛 赛时记录
    前言考试期间,只需要考虑考试策略即可,别的都别管了先打暴力,想正解不超过\(40\\rm{min}\),最后拼高档/正解,冷静,认真分析看题现在看到不按难度排序已经免疫了,感觉是防锅专用语?\(\rm{T1}\)题意非常清楚,可能挖下性质还是可以做的\(\rm{T2}\)我不到啊,但......
  • CW信号的正交解调
    1.CW信号  CW可以叫做等幅电报,它通过电键控制发信机产生短信号"."(点)和长信号"--"(划),并利用其不同组合表示不同的字符,从而组成单词和句子。  CW信号可以看作一种幅度调制信号,类似于幅移键控(2ASK信号)其携带的信息保存在其幅度中,通过改变载波的幅度来实现基带数据的传输。其函......
  • ACwing 1524. 最长回文子串
    ACwing1524.最长回文子串因为这个题的数据范围只有1000,所以能O(n)枚举,枚举回文子串的中点,然后向两边延展,看看极限长度是多少,注意每次要区分奇数长度字串和偶数长度字串,两种的计算方式不一样。#include<iostream>#include<cstdio>#include<cstdlib>intmain(){ std::s......
  • 12.19 CW 模拟赛 T1. 烟花
    思路转化题意移步赛时记录详细题解见题解下载好的那么主要问题仍然是怎样做才能扔掉后效性,乍一看是不可能的,但是我们可以慢慢的考虑首先我们需要利用有效时间段\(\leq500\)这个条件,我们考虑建出每种选择的情况,再按照树上的仇恨关系建出图具体的,对于每一种\([j......
  • 12.19 CW 模拟赛 T2. 数
    思路赛时读错题了,虽然说读对了不一定能做出来,但是还是比较可惜首先阐述一下题意,对\(S\)数组进行插入和删除操作,每次询问让你用\(T\)中的质数组合出\(x\),然后将\(S\)中的数乘以\(x\)之后求最多的完全立方数个数那么显然的,我们对于每一个数,都可以拆成质......
  • 12.19 CW 模拟赛 赛时记录
    前言考试的时候只需要管考试的策略就行了,其他的想他干嘛看题一般来说,涨信心模拟赛都不会涨信心像一位故人出的题?\(\rm{T1}\)相信自己,冲一下\(\rm{T2}\)看不懂\(\rm{T3}\)博弈\(\rm{T4}\)困难\(\rm{T1}\)机房两青轴是真的蚌埠思路转化题意,对于\(N\)条线......
  • 12.17 CW 模拟赛 赛时记录
    前言这一次又来更新比赛策略讲真打了这么久,还没有一个好的比赛策略真的是抽象回去吃点感冒药看题怎么\(\rm{T1\T2}\)是一个题\(\rm{T1}\)可能是\(\rm{dp}\)题\(\rm{T3}\)有些不好做\(\rm{T4}\)这种题一般都不会做,能骗多少是多少\(\rm{T1}\)思路转化题意......
  • 12.17 CW 模拟赛 T4. 记忆碎片
    思路转化题意,问你在一个带权无向完全图中,如何填上\(w_i\in\left[1,\frac{n\cdot(n-1)}{2}\right]\),使得其最小生成树上的边权为给定的\(n-1\)个数考虑模仿\(\rm{kruskal}\)的方式,令\(f_S\)表示当前点集为\(S\),每次转移,如果当前边权需要在最小生......