2024.11.15 CW 模拟赛

题面

T1

诈骗题, 考虑贪心.

容易发现一个合法的方阵每一行或者每一列一定形如 "ABAB..." 或者 "BABA...".

那么可以对横行和纵列分别进行贪心.

最后取 \(\max\) 即可.

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

#include "iostream"

using namespace std;

template <typename T>
inline void read(T &x)
{
    x = 0;
    char ch = getchar();
    while (!isdigit(ch))
        ch = getchar();
    while (isdigit(ch))
        x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return;
}

const int N = 1e3 + 1;

short n, a[N][N];
int ans1 = 0, ans2 = 0;

inline void init()
{
    read(n);
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
            read(a[i][j]);
        int tmp1 = 0, tmp2 = 0;
        for (int j = 1; j <= n; j += 2)
            tmp1 += a[i][j];
        for (int j = 2; j <= n; j += 2)
            tmp2 += a[i][j];
        ans1 += max(tmp1, tmp2);
    }

    return;
}

inline void calculate()
{
    for (int j = 1; j <= n; ++j)
    {
        int tmp1 = 0, tmp2 = 0;
        for (int i = 1; i <= n; i += 2)
            tmp1 += a[i][j];
        for (int i = 2; i <= n; i += 2)
            tmp2 += a[i][j];
        ans2 += max(tmp1, tmp2);
    }

    cout << max(ans1, ans2) << '\n';
    return;
}

inline void solve()
{
    init();
    calculate();
    return;
}

int main()
{
    solve();
    return 0;
}

T2

动态规划.
首先可以跑一遍 \(Floyd\) 计算最短路, 即用按键 \(j\) 替代按键 \(i\) 的最小天数.

接下来定义状态.

\(f_i\) 为到前 \(i\) 位是合法序列的最小花费.

那么有转移方程:

\[f_i=\min(f_j+cost[j,i,l]),\ 1 \le j \le i-k \]

其中 \(cost[j,i,l]\) 表示将 \(j\)\(i\) 全部变成按键 \(l\) 的花费.

这个可以使用前缀和进行优化.

届时时间复杂度为 \(\mathcal{O}(n^2 m^2)\), 不足以通过此题.

可以发现瓶颈是在枚举 \(f_j+cost[j,i,l]\).

因其具有单调性, 我们记 \(pre_c=dp_{i-k}-sum_{i-k,j},\ 1 \le c \le m\)

所以方程可以优化为:

\[dp_i=\min(pre_c+sum_{i,c}),\ 1 \le c \le m \]

时间复杂度 \(\mathcal{O}(nm)\).

#include "iostream"
#include "cstring"

using namespace std;

const int M = 27, N = 1e5 + 10;

int n, m, k;
int f[M][M];
int pos[N];
long long sum[N][M];
string s;

inline void init()
{

    cin >> n >> m >> k;
    cin >> s, s = " " + s;

    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= m; ++j)
            cin >> f[i][j];

    for (int K = 1; K <= m; ++K)
        for (int i = 1; i <= m; ++i)
            for (int j = 1; j <= m; ++j)
                f[i][j] = min(f[i][K] + f[K][j], f[i][j]);

    for (int i = 1; i <= n; ++i)
    {
        pos[i] = s[i] - 'a' + 1;
        for (int j = 1; j <= m; ++j)
            sum[i][j] = sum[i - 1][j] + f[pos[i]][j];
    }

    return;
}

long long dp[N], pre[N];

inline void calculate()
{

    memset(dp, 63, sizeof dp);
    dp[0] = 0;
    memset(pre, 63, sizeof pre);

    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= m; ++j)
        {
            if (i >= k)
                pre[j] = min(pre[j], dp[i - k] - sum[i - k][j]);
            dp[i] = min(dp[i], pre[j] + sum[i][j]);
        }
    }

    cout << dp[n] << '\n';

    return;
}

inline void solve()
{
    init();
    calculate();
    return;
}

int main()
{

    cin.tie(nullptr)->ios::sync_with_stdio(false);

    solve();

    return 0;
}

T3

动态规划, 质因数分解

阅读题面, 容易发现原题意可以转化成:

当前有 \(cnt\) 个环, 且 \(\sum_{i=1}^{cnt} len_i=n\), 求 \(\sum \rm{lcm} (len_i)\).

而对于每一种情况, \(\rm{lcm}(len_i)\) 的值只与每个环长度的质因数有关, 是每一个质因数取最高次幂乘起来.
所以我们可以考虑枚举素数进行 dp.

\(f_{i,j}\) 表示当前所有长度不为 \(1\) 的环总长度为 \(i\), 每个环长度中最大的质因子不超过 \(p_j\) 的答案.

考虑枚举 \(p_j\) 的次幂作为新的环的长度(因为如果加到原来的环之后算答案回去重所以是一样的),
故有以下转移方程:

\[f_{i,j}=f_{i,j-1}+ \sum_{p_j^k \le i} p^k_j f_{i-p_j^k,j-1} \]

可以 \(\mathcal{O}(n^2)\) 枚举.
注意 \(i\) 需要倒序枚举(背包).

另外附一个结论:
如果 \(p\) 是一个素数集(包括 1), \(c\) 是一个自然数集, \(\lvert c \rvert = \lvert p \rvert = m\), 那么

\[\forall i \in [1,m],\ len_i=p_i^{c_i} \]

这一类的拆分, 囊括了所有的答案.

  • \(Proof\)
    假设存在一种划分的 \(len_j\) 不符合上述条件, 记 \(len_j\)\(k\) 个质因数.
    那么可以把 \(len_j\) 拆成 \(k\) 个符合上述条件的环, 对 \(\rm{lcm}\) 没有影响.
    并且可以证明拆出来的 \(k\) 个环的总长度不大于 \(len_j\) (类比 \(a,b \ge 2 \rightarrow ab \ge a+b\)).
#include "iostream"
#include "bitset"

using namespace std;

const int N = 1e4 + 1, M = 4e3 + 1;

short cnt = 0, p[M];
bitset<N> vis;

inline void calc_prime(int n)
{
    for (short i = 2; i <= n; ++i)
    {
        if (!vis[i])
            p[++cnt] = i;
        for (short j = 1; i * p[j] <= n; ++j)
        {
            vis[i * p[j]] = 1;
            if (!(i % p[j]))
                break;
        }
    }
    return;
}

short n;
int mod;

inline void init()
{
    cin >> n >> mod;
    calc_prime(n);
    return;
}

int f[N];

inline void calculate()
{

    f[0] = 1;

    for (int i = 1; i <= cnt; ++i)
        for (int j = n; j >= 1; --j)
            for (int k = p[i]; k <= j; k *= p[i])
                f[j] = (f[j] + 1ll * f[j - k] * k % mod) % mod;

    int ans = 0;

    for (int i = 1; i <= n; ++i)
        ans = (ans + f[i]) % mod;

    cout << ans + 1 << '\n';

    return;
}

inline void solve()
{
    init();
    calculate();
    return;
}

int main()
{
    solve();
    return 0;
}
posted @   Steven1013  阅读(4)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示