2024.11.20 CW 模拟赛

题面 & 题解

T1

算法

区间动态规划.

思路

考虑两个人在一轮中的操作, 先手(执行删除操作的人)希望得分最小化, 后手(执行取数操作的人)希望得分最大化.

显然有如下转移方程:

\[f_{l,r} = \min^r_{i=l} (\max(sum(l,i-1)+f_{i+1,r},sum(i+1,r)+f_{l,i-1})) \]

边界条件 \(f_{i,i}=0\).
这里我采用的是记忆化搜索实现.

#include "iostream"

using namespace std;

const int N = 5e2 + 10;

int n;
int a[N], pre[N];
int f[N][N];

int solve(int l, int r)
{ 
    if (f[l][r] != 1e9)
        return f[l][r];
    if (l >= r)
        return 0;
    else if (r - l + 1 == 2)
        return min(a[l], a[r]);
    for (int i = l; i <= r; i++)
        f[l][r] = min(f[l][r], max(solve(l, i - 1) + pre[r] - pre[i], solve(i + 1, r) + pre[i - 1] - pre[l - 1]));
    return f[l][r];
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        pre[i] = pre[i - 1] + a[i];
        for (int j = 1; j <= n; j++)
            f[i][j] = 1e9;
    }
    cout << solve(1, n) << '\n';
    return 0;
}

T2

算法

二分, 图论.

思路

容易发现, 当自身武力值越高, 能够通过的边越多, 能够收集的货物总量也就越多.

所以答案具有单调性, 可以考虑二分答案.

\(check\) 的时候如果遇到比当前武力值大的边直接跳过, 否则继续 dfs.

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

#include "iostream"

using namespace std;

typedef pair<int, int> pii;
#define getchar getchar_unlocked

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 = 1e6 + 1;

int n, W, mx = -1;
int a[N];
basic_string<pii> g[N];

inline void init()
{
    read(n), read(W);
    for (int i = 2; i <= n; ++i)
        read(a[i]);
    for (int i = 1; i < n; ++i)
    {
        int u, v, w;
        read(u), read(v), read(w);
        g[u] += {v, w};
        g[v] += {u, w};
        mx = max(mx, w);
    }
    return;
}

long long tot = 0;

void dfs(int u, int fa, int up)
{
    for (pii i : g[u])
    {
        int v = i.first, w = i.second;
        if (v == fa)
            continue;
        if (w > up)
            continue;
        tot += a[v];
        dfs(v, u, up);
    }
    return;
}

inline bool check(int x)
{
    tot = 0;
    dfs(1, 0, x);
    return tot >= W;
}

inline void calculate()
{
    int l = 0, r = mx, mid, ans;
    while (l <= r)
    {
        mid = l + r >> 1;
        if (check(mid))
            ans = mid, r = mid - 1;
        else
            l = mid + 1;
    }
    printf("%d\n", ans);
    return;
}

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

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

T3

算法

动态规划及其优化, ST 表 / 线段树, 单调队列.

思路

先考虑 30pts.

\(f_{i,j}\) 表示到第 i 个的时候已经分了 j 组.

则有以下方程:

\[f_{i,j} = \min (f_{k,j-1} + \max(k+1,i) - \min(k+1,i) + sum(k+1,i)) \]

这样转移是 \(\mathcal{O}(n^3)\) 的, 其中查询 \(\min,\max\) 可以用 ST 表/线段树优化.

60pts.
可以考虑费用提前计算, 删除掉 \(j\) 这维, 则转移方程如下:

\[f_i=\min_{j<i}(f_j+sum(j+1,n)+cost(j+1,i)) \]

转移是 \(\mathcal{O}(n^2)\) 的, 还是不足以通过此题.

100pts.
参考这篇博客 QAQ.

优化大意是将一些无用的状态剔除, 不再使用其进行转移.

#include "iostream"
#include "cmath"
#include "deque"

using namespace std;

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

const int N = 1e5 + 1;

int n, W;
int a[N];

class ST
{
protected:
    int fmn[N][21], fmx[N][21];

public:
    inline void init()
    {
        for (int i = 1; i <= n; ++i)
            fmn[i][0] = fmx[i][0] = a[i];
        int p = log2(n);
        for (int k = 1; k <= p; ++k)
            for (int s = 1; s + (1 << k) <= n + 1; ++s)
            {
                fmx[s][k] = max(fmx[s][k - 1], fmx[s + (1 << k - 1)][k - 1]);
                fmn[s][k] = min(fmn[s][k - 1], fmn[s + (1 << k - 1)][k - 1]);
            }
        return;
    }
    inline int query(int l, int r)
    {
        int k = log2(r - l + 1);
        int x = max(fmx[l][k], fmx[r - (1 << k) + 1][k]),
            y = min(fmn[l][k], fmn[r - (1 << k) + 1][k]);
        return x - y;
    }
} st;

long long sum[N];

inline void init()
{
    read(n), read(W);
    for (int i = 1; i <= n; ++i)
        read(a[i]), sum[i] = sum[i - 1] + a[i];
    st.init();
    return;
}

long long f[N];
deque<int> q;

inline void calculate()
{
    f[0] = sum[n];
    q.push_back(0);
    for (int i = 1; i <= n; ++i)
    {
        while (!q.empty() and sum[i] - sum[q.front()] > W)
            q.pop_front();
        f[i] = 1e18;
        for (int x : q)
            f[i] = min(f[i], f[x] + sum[n] - sum[i] + st.query(x + 1, i));
        while (!q.empty() and f[i] <= f[q.back()])
            q.pop_back();
        q.push_back(i);
    }
    printf("%lld\n", f[n]);
    return;
}

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

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