Loading

12.17 CW 模拟赛 赛时记录

前言

这一次又来更新比赛策略

讲真打了这么久, 还没有一个好的比赛策略真的是抽象

回去吃点感冒药

看题

怎么 \(\rm{T1 \ T2}\) 是一个题

\(\rm{T1}\) 可能是 \(\rm{dp}\)

\(\rm{T3}\) 有些不好做

\(\rm{T4}\) 这种题一般都不会做, 能骗多少是多少

\(\rm{T1}\)

思路

转化题意

对于两组, 一组有 \(n\) 个点, 另一组有 \(m\) 个点, 求如何从两组中选取点, 使得 \(w\) 之和相等的情况下, \(\sum v\) 最大

本身就很像 \(\rm{dp}\) , 我们考虑推一下朴素的方程

\(f_{i, j}\) 表示考虑到第 \(i\) 个人, 此时两个组织间的 \(\sum w\) 之差为 \(j\) , 最大的精彩度

考虑转移,

\[f_{i, j} = \begin{cases} \begin{aligned} & f_{i - 1, j} \\ & f_{k, j \pm w_i} + v_i \\ \end{aligned} \end{cases}\\ \]

其中 \(\pm\) 由当前选择的位置与 \(k\) 是否在同一组而决定


又想了一些杂七杂八的事情, 先放过这个题, 我们看一下 \(30 \%\) 怎么拿
注意以后即使不考试, 也要时间分配好


对于 \(n, m\) 很小的情况下, 我们考虑预处理出可能的 \(\sum w\) 值对应的最大 \(v\), 这很像一个背包

那么我们将这两个背包组合起来, 就得到了答案

整个的复杂度为 \(\mathcal{O} (n^2w)\)

先打, 时间不足后面再来找高分

实现

框架

先做两个背包, 然后枚举合并答案即可

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 120;
const int MAXC = 52064;

int n, m;
struct student {
    int w; // 能力值
    int v; // 精彩度
} stu1[MAXN], stu2[MAXN];

int maxc = 0;
int dp1[MAXC], dp2[MAXC];
/*两边各做一次背包*/
void init()
{
    for (int i = 1; i <= 50000; i++) dp1[i] = dp2[i] = -0x3f3f3f3f;

    int C = 0; for (int i = 1; i <= n; i++) C += stu1[i].w;
    maxc = std::max(maxc, C);
    for (int i = 1; i <= n; i++) {
        for (int j = C; j >= stu1[i].w; j--) {
            dp1[j] = std::max(dp1[j], dp1[j - stu1[i].w] + stu1[i].v);
        }
    }

    C = 0; for (int i = 1; i <= m; i++) C += stu2[i].w;
    maxc = std::min(maxc, C);
    for (int i = 1; i <= m; i++) {
        for (int j = C; j >= stu2[i].w; j--) {
            dp2[j] = std::max(dp2[j], dp2[j - stu2[i].w] + stu2[i].v);
        }
    }
}

signed main()
{
    scanf("%lld %lld", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld %lld", &stu1[i].w, &stu1[i].v);
    for (int i = 1; i <= m; i++)
        scanf("%lld %lld", &stu2[i].w, &stu2[i].v);

    init();

    int Ans = 0;
    for (int i = 0; i <= maxc; i++) {
        if (dp1[i] != -0x3f3f3f3f && dp2[i] != -0x3f3f3f3f)
            Ans = std::max(Ans, dp1[i] + dp2[i]);
    }

    printf("%lld", Ans);

    return 0;
}

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

\(\rm{T2}\)

思路

转化题意,

对于一颗树, 我们定义一个花费是指从 \(m\) 个互不相同的点到一个点, 路径长度之和的最小值
求出对于任意一种 \(m\) 节点的位置, 花费的总和

看上去就不好做

先考虑对于确定的 \(m\) 节点的位置, 如何计算花费

好吧看起来不可做

那么开始骗

对于 \(20 \sim 30 \%\) 的数据, 我们讨论 \(m\) 个点的分布 \(\mathcal{O} (2 ^ n)\) , 计算答案需要枚举集合点 \(x\) \(\mathcal{O} (n)\) , 然后求路径长度之和 \(\mathcal{O} (m)\)

对于 \(m = 2\) 的情况下, 集合点即为 \(\rm{LCA}\) , 怎么求?
不会, 直接不要了

对于链的情况下, 选取中间的点作为集合点一定最优, 那么对于每一个集合点, 我们考虑

算了只要 \(20 \sim 30 \%\)

实现

框架

先在外层枚举选择点的情况, 然后进去计算

代码

#include <bits/stdc++.h>
const int MAXN = 30;
const int MOD = 1e9 + 7;

int n, m;
int Sum = 0;

struct Tree
{
    struct node {
        int to, next;
    } Edge[MAXN << 1];
    int head[MAXN], Edge_cnt;
    void head_init() { for (int i = 1; i <= n; i++) head[i] = -1; }

    void addedge(int u, int v) {
        Edge[++Edge_cnt].to = v, Edge[Edge_cnt].next = head[u];
        head[u] = Edge_cnt;
    }
} tree;

bool tag[MAXN]; // 标记是否有点

int Ans = 0;
int NowAns = 0;
void dfs(int now, int dep, int fa) {
    if (tag[now]) (NowAns += dep) %= MOD;
    for (int i = tree.head[now]; ~i; i = tree.Edge[i].next) {
        int nowto = tree.Edge[i].to;
        if (nowto == fa) continue;
        dfs(nowto, dep + 1, now);
    }
}

void solve(int State)
{
    int cnt = 0;
    memset(tag, false, sizeof(tag));
    while (State) {
        if (State & 1) tag[++cnt] = true;
        else tag[++cnt] = false;
        State >>= 1;
    }

    Ans = 0x3f3f3f3f, NowAns = 0;

    /*外层枚举集合点*/
    for (int i = 1; i <= n; i++) {
        NowAns = 0;
        dfs(i, 0, -1);
        Ans = std::min(Ans, NowAns);
    }
}

int main()
{
    scanf("%d %d", &n, &m);
    tree.head_init();
    for (int i = 1; i < n; i++) {
        int read; scanf("%d", &read);
        tree.addedge(i + 1, read), tree.addedge(read, i + 1);
    }

    /*外层枚举点的情况*/
    for (int State = 0; State < (1 << n); State++) {
        if (std::__popcount(State) != m) continue;

        solve(State);
        (Sum += Ans) %= MOD;
    }

    printf("%d", Sum);

    return 0;
}

\(\rm{T3}\)

粗略看一下部分分很足

直接开始, 很显然的我们可以 \(\mathcal{O} (\omega \lambda^2)\) 的枚举时间, 其中 \(\omega = 24, \lambda = 60\)

然后我们就可以计算此时的答案, 这个的复杂度是 \(\mathcal{O} (\omega \lambda^2 n)\)

但我时间可能不够了, 模拟题你是坠棒的

所以简单的考虑一下 \(40 \%\)

总结

怎么会有 \(\rm{T3}\) 放模拟的题啊???

这我骗鸡毛啊

留了 \(30 \ \rm{min}\) 来看 \(\rm{T3}\) 结果是模拟????

posted @ 2024-12-18 18:32  Yorg  阅读(26)  评论(0)    收藏  举报