zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    构建单页面应用
    chrome进入控制台时自动进入断点模式的解决方法
    git安装--linux下的安装
    express响应前端ajax请求
    nodejs链接mongodb数据库
    Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式)介绍
    Nginx中如何限制某个IP同一时间段的访问次数
    nodejs && apidoc
    apidoc
    android sdk
  • 原文地址:https://www.cnblogs.com/daklqw/p/13614729.html
Copyright © 2011-2022 走看看