【UR #7】水题走四方

看,这个鸽子又更博了!(这段内容和题目无关)

突然细节能力不行,这几天各种细节写挂……有些还是各种难以拍出来的……

这几天,我:

  • 线段树区间查询用 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;
}
posted @ 2020-09-04 16:50  daklqw  阅读(306)  评论(0编辑  收藏  举报