前言
这一次又来更新比赛策略
讲真打了这么久, 还没有一个好的比赛策略真的是抽象
回去吃点感冒药
看题
怎么 \(\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}\) 结果是模拟????
标签:赛时,int,30,12.17,集合点,rm,mathcal,CW,MAXN From: https://www.cnblogs.com/YzaCsp/p/18615664