• Plutotree


    题目蓝链

    Solution

    虽然这题加了很多边,但本质上还是一棵树,我们只需要维护一下链上的和与最大值,然后直接树形DP

    但不过这道题的DP比较独特,因为一个节点的DP值可以从它的父亲更新过来。那么这个DP是不是就是有后向性了呢?

    其实我们只需要DP两遍就可以了,第一遍DP处理出一个点到它的子树内的最优值。第二遍的时候,对于每一个点(i)记录一下一个权值(dp_i - pre_i)((pre_i)为当前点到根的前缀和),在更新(i)号点的时候,我们在它的所有祖先中找一个这个权值最小的点(j),然后用(dp_j - pre_j + pre_i)来更新当前点就可以了

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int maxn = 1e5 + 10;
    const int inf = 0x3f3f3f3f;
    
    int n, q;
    
    namespace Tree {
    
    	vector<int> g[maxn];
    	int d[maxn], fa[maxn][17], Max[maxn][17];
    	int w[maxn], pre[maxn];
    	pii dp[maxn];
    
    	void input() {
    		for (int i = 2; i <= n; i++) g[read()].push_back(i);
    		for (int i = 1; i <= n; i++) w[i] = read();
    	}
    
    	void dfs1(int now, int f) {
    		dp[now] = (pii){inf, inf}, pre[now] = pre[f] + w[now];
    		d[now] = d[f] + 1, fa[now][0] = f, Max[now][0] = max(w[now], w[f]);
    		for (int i = 1; i <= 16; i++) {
    			fa[now][i] = fa[fa[now][i - 1]][i - 1];
    			Max[now][i] = max(Max[now][i - 1], Max[fa[now][i - 1]][i - 1]);
    		}
    		for (int son : g[now]) {
    			dfs1(son, now);
    			pii tmp = (pii){dp[son].first + w[now], min(dp[son].second, -w[now])};
    			dp[now] = min(dp[now], tmp);
    		}
    		if (dp[now] == (pii){inf, inf}) dp[now] = (pii){w[now], -w[now]};
    	}
    
    	void dfs2(int now, pii pp) {
    		pii tmp = (pii){pp.first + pre[now], min(pp.second, -w[now])};
    		dp[now] = min(dp[now], tmp);
    		tmp = (pii){dp[now].first - pre[now], dp[now].second};
    		pp = min(pp, tmp);
    		for (int son : g[now]) dfs2(son, pp);
    	}
    
    	int lca(int x, int y) {
    		if (d[x] < d[y]) swap(x, y);
    		for (int i = 16; ~i; i--)
    			if (d[fa[x][i]] >= d[y]) x = fa[x][i];
    		if (x == y) return x;
    		for (int i = 16; ~i; i--)
    			if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    		return fa[x][0];
    	}
    
    	int sum(int x, int y) {
    		int f = lca(x, y), ff = fa[f][0];
    		return pre[x] + pre[y] - pre[f] - pre[ff];
    	}
    
    	int get_max(int x, int y) {
    		int MAX = max(w[x], w[y]), ff = lca(x, y);
    		for (int i = 16; ~i; i--)
    			if (d[fa[x][i]] >= d[ff]) MAX = max(MAX, Max[x][i]), x = fa[x][i];
    		for (int i = 16; ~i; i--)
    			if (d[fa[y][i]] >= d[ff]) MAX = max(MAX, Max[y][i]), y = fa[y][i];
    		return MAX;
    	}
    
    	void query(int x, int y) {
    		pii ans = (pii){sum(x, y), -get_max(x, y)};
    		pii t1 = (pii){dp[x].first + pre[y], min(-get_max(y, 1), dp[x].second)};
    		pii t2 = (pii){dp[y].first + pre[x], min(-get_max(x, 1), dp[y].second)};
    		pii t3 = (pii){dp[x].first + dp[y].first + w[1], min(-w[1], min(dp[x].second, dp[y].second))};
    		ans = min(ans, t1), ans = min(ans, t2), ans = min(ans, t3);
    		printf("%d %d
    ", ans.first, -ans.second);
    	}
    
    }
    
    int main() {
    	freopen("plutotree.in", "r", stdin);
    	freopen("plutotree.out", "w", stdout);
    
    	n = read(), q = read();
    	Tree::input();
    	Tree::dfs1(1, 0), Tree::dfs2(1, (pii){inf, inf});
    
    	while (q--) {
    		int x = read(), y = read();
    		Tree::query(x, y);
    	}
    
    	return 0;
    }
    

    Summary

    我是真的菜,一开始一直以为这题不能DP....

    这是我第一次接触这种多次更新答案的树形DP题,又学到了一个新的技巧

  • 相关阅读:
    【C#】C#获取文件夹下的所有文件
    6 云计算系列之Nova安装与配置
    5 云计算系列之glance镜像服务安装
    4 云计算系列之Openstack简介与keystone安装
    3大数据挖掘系列之文本相似度匹配
    6 Django系列之关于models的sql语句日常用法总结
    2 python大数据挖掘系列之淘宝商城数据预处理实战
    5 Django系列之通过list_display展示多对多与外键内容在admin-web界面下
    1 python大数据挖掘系列之基础知识入门
    4 django系列之HTML通过form标签来同时提交表单内容与上传文件
  • 原文地址:https://www.cnblogs.com/xunzhen/p/9692849.html
Copyright © 2020-2023  润新知