Codeforces 294E Shaass the Great 树形dp(水

题目链接:点击打开链接

题意:

给定n个点的树,任意拆掉一条边,得到2个子树,再用刚拆掉的边把这两个子树连起来。

得到新的树,这个树的权值为任意两个点间的距离和。

使得新的树权值最小。输出这个权值。

枚举拆掉的边(u,v)

得到2个以u为根的子树和以v为根的子树

计算每条边对答案的贡献,拆掉的边贡献就是siz[u]*siz[v]*edge[u,v].dis

剩下的就是计算如何连接2个子树使得权值和最小。

对于子树中的一条边x, y,若已知两端的节点数为i,j,则这条边对答案的贡献就是 i*j*edge[x,y].dis

新建的边实际上只有连接x点方向或者y点方向。

所以就能得到dp数组

dp[u][0]表示新建边连接u的子树时u这个子树的贡献。

dp[u][1]表示新建边连接的不是u的子树时的贡献。

java还是tle。。。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<math.h>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef long long ll;
const int N = 5050;
const int M = N * 2;
const ll inf64 =  1e18;
struct Edge{
	int from, to, dis, nex;
}edge[M << 1];
int head[N], edgenum;
void init_edge(){ for (int i = 0; i < N; i++)head[i] = -1; edgenum = 0; }
void add(int u, int v, int dis){
	Edge E = { u, v, dis, head[u] };
	edge[edgenum] = E;
	head[u] = edgenum++;
}
ll dp[N][2];
int siz[N];
int n;
void justgo(int u, int fa){
	siz[u] = 1;
	for (int i = head[u]; i != -1; i = edge[i].nex){
		int v = edge[i].to; if (v == fa)continue;
		justgo(v, u);
		siz[u] += siz[v];
	}
}
void dfs(int u, int fa, int root){
	dp[u][0] = inf64;
	dp[u][1] = 0;
	for (int i = head[u]; i != -1; i = edge[i].nex){
		int v = edge[i].to; if (v == fa)continue;
		dfs(v, u, root);
		dp[u][1] += dp[v][1] + (ll)edge[i].dis*siz[v] * (n - siz[v]);
	}
	for (int i = head[u]; i != -1; i = edge[i].nex){
		int v = edge[i].to; if (v == fa)continue;
		dp[u][0] = min(dp[u][0], dp[u][1] - (ll)edge[i].dis*siz[v] * (n - siz[v]) - dp[v][1] + min(dp[v][1], dp[v][0]) + (ll)edge[i].dis*(siz[root] - siz[v])*(n - siz[root] + siz[v]));
	}
}
int main(){
	init_edge();
	rd(n);
	for (int i = 1, u, v, d; i < n; i++){
		rd(u); rd(v); rd(d);
		add(u, v, d); add(v, u, d);
	}
	ll ans = inf64;
	for (int i = 0, u, v; i < edgenum; i += 2){
		u = edge[i].from; v = edge[i].to;
		justgo(u, v); justgo(v, u);
		dfs(u, v, u);
		dfs(v, u, v);
		ans = min(ans, min(dp[u][0], dp[u][1]) +
			min(dp[v][0], dp[v][1]) + (ll)edge[i].dis*siz[u] * siz[v]);
	}
	pt(ans);
	return 0;
}
时间: 2024-10-12 23:40:31

Codeforces 294E Shaass the Great 树形dp(水的相关文章

URAL 1039 Anniversary Party 树形DP 水题

1039. Anniversary Party Time limit: 0.5 secondMemory limit: 8 MB Background The president of the Ural State University is going to make an 80'th Anniversary party. The university has a hierarchical structure of employees; that is, the supervisor rela

Codeforces 461B Appleman and Tree(树形dp)

题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每个节点的父亲节点,以及每个点的颜色(0表示白色,1表示黑色),切断这棵树的k条边,使得树变成k+1个联通分量,保证每个联通分量有且仅有1个黑色节点.问有多少种分割方法. 解题思路:树形dp,dp[i][0]和dp[i][1]分别表示子树一下的分割方法中,i节点所在联通块不存在黑节点和已经存在一个黑节点的方案数. #include <cstdio> #include <c

洛谷P1352 没有上司的舞会(树形DP水题)

题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了.所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数. 输入输出格式 输入格式: 第一行一个整数N.(1<=N<=6000) 接下来N行,第i+1行表示i号职员的快乐指数Ri.(-128<=Ri

Codeforces 462D Appleman and Tree 树形dp

题目链接:点击打开链接 题意: 给定n个点的树, 0为根,下面n-1行表示每个点的父节点 最后一行n个数 表示每个点的颜色,0为白色,1为黑色. 把树分成若干个联通块使得每个联通块有且仅有一个黑点,问有多少种分法(结果mod1e9+7) 思路: 树形dp,每个点有2个状态,已经归属于某个黑点和未归属于某个黑点. #include <cstdio> #include <vector> #include <iostream> using namespace std; #de

Codeforces 543D Road Improvement(树形DP+乘法逆元)

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案.然后解法也不难想: dp0[u]表示只考虑以u结点为根的子树的方案数 dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数 有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1).这两部分求法如下,画

Codeforces 627D Preorder Test(二分+树形DP)

题意:给出一棵无根树,每个节点有一个权值,现在要让dfs序的前k个结点的最小值最大,求出这个值. 考虑二分答案,把>=答案的点标记为1,<答案的点标记为0,现在的任务时使得dfs序的前k个节点都为1. 考虑树形DP. 用dp[u]表示从节点u开始在子树中进行dfs最多可以经过多少个为1的结点,显然,若某一个子树中节点全为1,那么这个可以加到dp[u]中,此外还可以在不全为1的子树中挑选一个加到dp[u]上. 那么答案就是从标记为1的节点当做根,选两颗不完全子树和所有的完全子树(包括从父亲向上的

POJ 2342 &amp;&amp;HDU 1520 Anniversary party 树形DP 水题

一个公司的职员是分级制度的,所有员工刚好是一个树形结构,现在公司要举办一个聚会,邀请部分职员来参加. 要求: 1.为了聚会有趣,若邀请了一个职员,则该职员的直接上级(即父节点)和直接下级(即儿子节点)都不能被邀请 2.每一个员工都有一个兴奋值,在满足1的条件下,要使得邀请来的员工的兴奋值最高 输出最高的兴奋值. 简单的树形DP dp[i][1]:表示以i为根的子树,邀请节点i的最大兴奋值 dp[i][0]:表示以i为根的子树,不邀请节点i的最大兴奋值 先根据入度找出整棵树的根节点, 然后一次DF

Codeforces 919D Substring (拓扑排序+树形dp)

题目:Substring 题意:给你一个有向图, 一共有n个节点 , m条变, 一条路上的价值为这个路上出现过的某个字符最多出现次数, 现求这个最大价值, 如果价值可以无限大就输出-1. 题解:当这个有向图构成一个环的时候就会使得值无限大,所以先用拓扑排序判断一下有没有环,如果有环直接输出-1, 如果没有环就再使用树形dp并记忆化存数,来找到最大值. 代码: 1 #include<cstring> 2 #include<iostream> 3 using namespace std

树形DP水题系列(1):FAR-FarmCraft [POI2014][luogu P3574]

题目 大意: 边权为1 使遍历树时到每个节点的时间加上点权的最大值最小 求这个最小的最大值 思路: 最优化问题 一眼树形DP 考虑状态设立 先直接以答案为状态 dp[u] 为遍历完以u为根的子树的答案 再考虑状态转移 dp[u]=MAX(dp[to]+1,siz+dp[to]);siz为枚举子树到以to为节点的子树时之前已遍历的总时间 很明显这个转移过来的dp值的最优化依赖于子树遍历的顺序 所以我们需要找到一种最优的子树遍历顺序来使每个子树得到最优的dp值来更新 我们通过观察可以发现 交换任意两