• bzoj3252 攻略 贪心+dfs序+线段树


    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=3252

    题解

    有一个非常显然的贪心思路:每次选择目前走到那儿能够获得的新权值最大的点。

    证明的话,因为走过的点不再计入贡献,所以不这样走不可能有更优的。

    考虑怎么维护每个点能够获得的新点权的最大值。

    因为每个点只能做一次贡献,所以走过去以后对整个子树的作用都消失了,可以使用线段树区间修改。

    还是因为每个点只能做一次贡献,所以每一次修改可以暴力跳父节点,直到跳到已经做过贡献的点为止。


    时间复杂度 (O((n+m)log n))

    #include<bits/stdc++.h>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I> inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 200000 + 7;
    
    #define lc o << 1
    #define rc o << 1 | 1
    
    int n, k, dfc;
    int a[N], f[N], dfn[N], pre[N], siz[N], vis[N];
    ll dis[N];
    
    struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
    inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
    inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
    
    struct Node { ll add, max; int pos; } t[N << 2];
    inline void pushup(int o) {
    	t[o].max = 0;
    	if (smax(t[o].max, t[lc].max)) t[o].pos = t[lc].pos;
    	if (smax(t[o].max, t[rc].max)) t[o].pos = t[rc].pos;
    	t[o].max += t[o].add;
    	// dbg("o = %d, t[o].max = %lld, t[o].pos = %d
    ", o, t[o].max, t[o].pos);
    }
    inline void build(int o, int L, int R) {
    	if (L == R) return t[o].max = dis[pre[L]], t[o].pos = pre[L], (void)0;
    	int M = (L + R) >> 1;
    	build(lc, L, M), build(rc, M + 1, R);
    	pushup(o);
    }
    inline void qadd(int o, int L, int R, int l, int r, int k) {
    	if (l <= L && R <= r) return t[o].max += k, t[o].add += k, (void)0;
    	int M = (L + R) >> 1;
    	if (l <= M) qadd(lc, L, M, l, r, k);
    	if (r > M) qadd(rc, M + 1, R, l, r, k);
    	pushup(o);
    }
    
    inline void dfs(int x, int fa = 0) {
    	dfn[x] = ++dfc, pre[dfc] = x, siz[x] = 1, dis[x] = dis[fa] + a[x], f[x] = fa;
    	for fec(i, x, y) if (y != fa) dfs(y, x), siz[x] += siz[y];
    }
    
    inline void work() {
    	dfs(1), build(1, 1, n);
    	ll ans = 0;
    	while (k--) {
    		int p = t[1].pos;
    		ans += t[1].max;
    		// dbg("p = %d, val = %lld
    ", p, t[1].max + t[1].add);
    		while (p && !vis[p]) qadd(1, 1, n, dfn[p], dfn[p] + siz[p] - 1, -a[p]), vis[p] = 1, p = f[p];
    		// dbg("end: p = %d
    ", p);
    	}
    	printf("%lld
    ", ans);
    }
    
    inline void init() {
    	read(n), read(k);
    	for (int i = 1; i <= n; ++i) read(a[i]);
    	int x, y;
    	for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    android 联系数据库
    shuffle一个简单的过程叙述性说明
    每天的学习经验:SharePoint 2013 定义自己添加的产品清单。Callout菜单项、文档关注、SharePoint服务机端对象模型查询
    函数调用
    InputMonitor注意事项
    处理FTP上传成功推理
    用Web技术开发客户端(一)
    简析Chrome和Webkit的渊源
    历史在重演:从KHTML到WebKit,再到Blink
    开发者应当了解的WebKit知识
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj3252.html
Copyright © 2020-2023  润新知