看,这个鸽子又更博了!(这段内容和题目无关)
突然细节能力不行,这几天各种细节写挂……有些还是各种难以拍出来的……
这几天,我:
- 线段树区间查询用
if (l == r)
判断 - 线段树没考虑 (l > r)
- 线段树 tag 下传不给 tag 赋值
- 带权并查集初始化不写
sz[i] = 1
- 乘法不开 long long ,还是那种拍不出来的题。
- 要 swap 来赋值的 vector,硬是写了一个 insert,还不 clear,时空两爆炸。
- ……
“论我是怎么拿 au 的.jpg”,话说的好,接下来联赛翻车就好玩了……
毕竟官方题解够清楚了,这个提供一点看官方题解时的思路吧。
按照惯例,没题面。这道题让我自己想,我是真的想不出……
第一想法,就是直接 (f_i) 表示该子树的 DP 值,策略就是先遍历其他子树,然后再选择一个子树走。
然后就挂了……话说怎么在想的时候就发现这么做不行,那我是真的不会。
我们把留守的点叫做 (A),另一个叫 (B),那么如果 (A) 只有一个子节点,就可以两个点同时走,显然能节约。
通过一系列对拍,发现有些情况,(B) 要走到 (A) 的目标子树里会更优,这难以继续 DP。
但是显然,(A) 会有一堆停顿点,在停顿点的过程中,我们让 (B) 走,然后在 (B) 的最后一段,(A) 同时走,显然最后一段越深越优。
那么就相当于在树上一个点,DP 出它到根路径的划分,DP 方程显然:在 (A) 走到所有叶子的深度和,加上 A 的路径长度减去最后一段长度对 (0) 取 (max),具体见官方题解。
考虑优化,当不存在支链的时候,(A, B) 只能一起向下。
当存在支链的时候,考虑方案,如果 (A) 的路径长度比 (B) 最后一段长,那么我们还不如转移到更浅的点,方程里的 (max) 就去掉了。
考虑一个点转移给子树不太好算,那就算一个点从祖先转移。
由于段越短越好,那么只会选择最深的 (B) 不比 (A) 短的祖先。
那么剩下问题就是找这个祖先。
使用长剖技巧,发现只要记下最大和次大就很好维护了。
#include <bits/stdc++.h>
const int MAXN = 5e6 + 10;
typedef long long LL;
int n, fa[MAXN], dep[MAXN];
int cl[MAXN], up[MAXN];
LL sm[MAXN], f[MAXN];
int8_t deg[MAXN];
inline int isLeaF(int x) { return deg[x] == 0; }
inline int isOC(int x) { return deg[fa[x]] == 1; }
struct _ {
int a, b;
void ins(int x) {
if (x >= a) b = a, a = x;
else b = std::max(b, x);
}
int get(int x) {
return x == a ? b : a;
}
} ds[MAXN];
std::vector<int> hav[MAXN];
int main() {
std::ios_base::sync_with_stdio(false), std::cin.tie(0);
static char buf[MAXN << 1];
std::cin >> n >> buf;
for (int i = 0, rt = 0; i < n * 2; ++i) {
if (buf[i] == '(') {
static int u;
fa[++u] = rt;
dep[u] = dep[rt] + 1;
rt = u;
deg[fa[u]] = std::min(deg[fa[u]] + 1, 2);
} else rt = fa[rt];
}
for (int i = n; i > 1; --i) {
if (isLeaF(i)) {
cl[i] = 1;
sm[i] = dep[i];
ds[i].ins(dep[i]);
}
sm[fa[i]] += sm[i];
ds[fa[i]].ins(ds[i].a);
cl[fa[i]] += cl[i];
}
for (int i = n; i > 1; --i) {
int tl = ds[fa[i]].get(ds[i].a);
hav[i].push_back(i);
while (!hav[i].empty()) {
int u = hav[i].back();
if (dep[u] <= tl) {
up[u] = fa[i];
hav[i].pop_back();
} else break;
}
if (!hav[i].empty())
std::swap(hav[fa[i]], hav[i]);
}
memset(f, 0x3f, sizeof f);
f[1] = 0;
for (int i = 1; i <= n; ++i) {
if (i > 1 && isOC(i))
f[i] = std::min(f[i], f[fa[i]] + 1);
if (int u = up[i]) {
LL vl = f[u] + sm[u] - sm[i] - (LL) (cl[u] - cl[i]) * dep[u];
vl += std::max(dep[i] - ds[u].get(ds[i].a), 0);
f[i] = std::min(f[i], vl);
}
if (isLeaF(i))
f[0] = std::min(f[0], f[i]);
}
std::cout << f[0] << std::endl;
return 0;
}