• 洛谷 P7011 [CERC2013]Escape 题解


    一、题目:

    洛谷原题

    codeforces原题

    二、思路:

    这道题又是一道非常新奇的思维题。我觉得能想到这种算法的人真得很了不起。(反正我是看了题解才琢磨出来。)

    我们考虑类似树形 DP 的方式,从下向上转移。对于每个子树 (x),我们都维护一个集合 (S_x)(S_x) 中的元素是二元组 ((a,b)),代表如果你有至少 (a) 的血量,那么你的血量可以增加 (b)。现在我们来考虑,如果对于 (x) 的任意一个儿子 (y),我们都已知了 (S_y),如何求出 (S_x)

    1. 新建一个集合 (S)

    2. 将所有 (S_y) 中的所有元素全部塞到 (S) 中。

      这样肯定会使一些 ([a,a+b]) 产生交集,我们将这些 ([a,a+b]) 合并成大的区间。((*))

    3. 尝试将二元组 ((0,v_x)) 加入到集合 (S) 中。如果可以合并,就不断的从小到大进行合并。直到不能合并为止。

    最终怎么判断能不能 Escape 呢?我们可以新增一个点 (t'),让 (t')(t) 相连,并把 (t') 的权值设置成 (+infty)。最终看一下 (S_1) 中有没有元素的 (b) 大于等于 (+infty)

    可以用 set 来维护集合。合并的时候用启发式合并就可以了。

    但是,总感觉 ((*)) 这一步没有必要。

    事实上,((*)) 这一步的确没必要。仔细想想,我们其实没有必要使得所有的 ([a,a+b]) 两两之间没有交集。这并不影响我们的求解过程。

    所以,我们只需要用小根堆来维护集合,而不必使用 set。

    具体实现过程还必须看代码注释。

    三、代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <vector>
    
    using namespace std;
    #define FILEIN(s) freopen(s, "r", stdin);
    #define FILEOUT(s) freopen(s, "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    typedef pair<long long, long long> PLL;
    
    inline int read(void) {
    	int x = 0, f = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    	return f * x;
    }
    
    const int MAXN = 200005;
    const long long INF = 1e15;
    
    int n, t;
    long long val[MAXN];
    vector<int>linker[MAXN];
    
    priority_queue<PLL, vector<PLL>, greater<PLL> >q[MAXN];
    
    void dfs(int x, int fa) {
    	while (!q[x].empty()) q[x].pop();
    	for (auto &y : linker[x]) {
    		if (y == fa) continue;
    		dfs(y, x);
    		if (q[y].size() > q[x].size()) swap(q[x], q[y]); // 启发式合并
    		while (!q[y].empty()) { q[x].push(q[y].top()); q[y].pop(); }
    	}
    	PLL u = { 0, val[x] }, v;
    	while (!q[x].empty() && (u.second < 0/*条件1*/ || q[x].top().first <= u.first + u.second/*条件2*/)) {
    		v = q[x].top(); q[x].pop();
    		u = { max(u.first, v.first - u.second), u.second + v.second };
            // 根据while语句的条件,如果靠的是条件2进入的循环,那么max一定会取到u.first。
            // 如果靠的是条件1进入的循环,那么max一定会取到v.first-u.second,意思是提高v的门槛。
            // (注意所有的v.first都是非负的。)
    	}
    	if (u.second >= 0) q[x].push(u); // 如果该条件不满足,那么q[x]一定是空的。
    }
    
    int main() {
    	int testdata = read();
    	while (testdata --) {
    		n = read(); t = read();
    		for (int i = 1; i <= n; ++ i) val[i] = read();
    		for (int i = 1; i < n; ++ i) {
    			int x = read(), y = read();
    			linker[x].push_back(y); linker[y].push_back(x);
    		}
    		++ n; val[n] = INF; linker[t].push_back(n); linker[n].push_back(t);
    		dfs(1, 0);
    		if (!q[1].empty() && q[1].top().first == 0 && q[1].top().second >= INF) puts("escaped");
    		else puts("trapped");
    
    		for (int i = 1; i <= n; ++ i) linker[i].clear();
    	}
    	return 0;
    }
    

  • 相关阅读:
    解决configure: error: C preprocessor "/lib/cpp" fails sanity check
    centos7.3(1611) 64位 离线安装gcc
    spring-boot 启动时候 出现异常:The bean 'xxx' could not be injected as a 'xx.xxxx' because it is a JDK dynami
    springboot 关于 Class path contains multiple SLF4J bindings.警告的解决
    统计学习方法笔记---1203、统计学习方法总结(3.学习策略、4.学习算法)
    统计学习方法笔记---1202、统计学习方法总结(1.适用问题、2.模型)
    统计学习方法笔记---1201、统计学习方法总结
    统计学习方法笔记---0、读大纲
    心得体悟帖---201204(consciousness)
    心得体悟帖---201204(interest)
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14946288.html
Copyright © 2020-2023  润新知