BZOJ1827 [Usaco2010 Mar]gather 奶牛大集会

题意:给定一棵树,求出树上的一点,使得树上的全部点到该点的距离之和最小。

思路:暴力显然是O(N^2)等死对吧。

我们首先将无根树转化为有根树,然后一边dfs求出f[i],size[i].

f[i]表示以i为根的子树中全部的点到i的距离之和,size[i]表示以i为根的子树的点数。

以下開始脑洞大开:

如今对于我们一開始的那个root,我们已经知道了答案。问题就是怎样高速的推知别的点作为根时的答案。

我们又一次进行一次dfs,当找到x时,我们用dp[fa[x]]+padis[x]*size[fa[x]]更新答案。

我们记录一下当前的dp[x],以及size[x].

每找到一个儿子son,向下dfs时,我们令dp[x]=dp[fa[x]]+size[fa[x]]*padis[x]+dp[x]-dp[son]-size[son]*padis[son],size[x]=size[fa[x]]+size[x]-size[son],然后再向下dfs.

不要问我为什么。。。

我的代码用的是更加脑洞大开的方法。。。

Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define N 100010
int head[N], next[N << 1], end[N << 1], len[N << 1];
void addedge(int a, int b, int _len) {
	static int q = 1;
	len[q] = _len;
	end[q] = b;
	next[q] = head[a];
	head[a] = q++;
}

int num[N];

long long dp[N];
int pa[N], padis[N], size[N];
void dfs(int x, int fa) {
	size[x] = num[x];
	for(int j = head[x]; j; j = next[j])
		if (end[j] != fa)
			pa[end[j]] = x, padis[end[j]] = len[j], dfs(end[j], x);
	for(int j = head[x]; j; j = next[j])
		if (end[j] != fa)
			dp[x] += dp[end[j]] + (long long)size[end[j]] * len[j], size[x] += size[end[j]];
}

long long res = 1LL << 60;
int presize[N], sufsize[N], addsize[N], sav[N], top;
long long pre[N], suf[N], add[N];
void work(int x) {
	long long ans = add[x] + (long long)addsize[x] * padis[x] + dp[x];
	if (ans < res)
		res = ans;

	register int i, j;
	top = 0;
	for(j = head[x]; j; j = next[j])
		if (end[j] != pa[x])
			sav[++top] = end[j];

	presize[0] = pre[0] = 0, sufsize[top + 1] = suf[top + 1] = 0;
	for(i = 1; i <= top; ++i)
		presize[i] = presize[i - 1] + size[sav[i]], pre[i] = pre[i - 1] + dp[sav[i]] + (long long)size[sav[i]] * padis[sav[i]];
	for(i = top; i >= 1; --i)
		sufsize[i] = sufsize[i + 1] + size[sav[i]], suf[i] = suf[i + 1] + dp[sav[i]] + (long long)size[sav[i]] * padis[sav[i]];
	for(i = 1; i <= top; ++i) {
		addsize[sav[i]] = addsize[x] + num[x] + presize[i - 1] + sufsize[i + 1];
		add[sav[i]] = add[x] + (long long)addsize[x] * padis[x] + pre[i - 1] + suf[i + 1];
	}

	for(j = head[x]; j; j = next[j])
		if (end[j] != pa[x])
			work(end[j]);
}
int main() {
	int n;
	scanf("%d", &n);

	register int i, j;
	for(i = 1; i <= n; ++i)
		scanf("%d", &num[i]);

	int a, b, x;
	for(i = 1; i < n; ++i) {
		scanf("%d%d%d", &a, &b, &x);
		addedge(a, b, x);
		addedge(b, a, x);
	}

	dfs(1, -1);
	work(1);

	printf("%lld", res);

	return 0;
}
时间: 2024-08-03 23:30:31

BZOJ1827 [Usaco2010 Mar]gather 奶牛大集会的相关文章

【树形DP/搜索】BZOJ 1827: [Usaco2010 Mar]gather 奶牛大集会

1827: [Usaco2010 Mar]gather 奶牛大集会 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 793  Solved: 354[Submit][Status][Discuss] Description Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会.每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个

BZOJ 1827: [Usaco2010 Mar]gather 奶牛大集会( dp + dfs )

选取任意一个点为root , size[ x ] 表示以 x 为根的子树的奶牛数 , dp一次计算出size[ ] && 选 root 为集会地点的不方便程度 . 考虑集会地点由 x 点向它的子节点 son 转移 , 那么以 son 为集会地点比以 x 为集会地点要多 dist( x , son ) * ( tot - size[ x ] ) - dist( x , son ) * size[ x ] = dist( x , son ) * ( tot - 2 * size[ x ] )

【BZOJ 1827】 [Usaco2010 Mar]gather 奶牛大集会

1827: [Usaco2010 Mar]gather 奶牛大集会 Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 722  Solved: 314 [Submit][Status][Discuss] Description Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会.每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意

BZOJ 1827: [Usaco2010 Mar]gather 奶牛大集会

Description Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会.当然,她会选择最方便的地点来举办这次集会.每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场.道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000).集会可以在N个农场中的任意一个举行.另外

【BZOJ】1827: [Usaco2010 Mar]gather 奶牛大集会

[算法]树型DP [题解] 两遍DFS,第一次得到所有节点子树的路径和,第二次给出除了该子树外其它部分的路径和,时时计算答案. long long!!! #include<cstdio> #include<cstring> #include<algorithm> #include<cctype> #define ll long long using namespace std; const int maxn=100010; struct edge{int v

BZOJ 1827 [Usaco2010 Mar]gather 奶牛大集会(树形DP)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1827 [题目大意] 给出一棵有点权和边权的树, 请确定一个点,使得每个点到这个点的距离乘上该点乘积的总和最小. [题解] 定1为根,我们先计算当这个点为1的时候的值,同时记录每个子树的size 之后我们再做一遍dfs,计算出每个点作为中心时候的答案 我们发现当一个点从父节点往子节点移动的时候 对于答案的变化是ans+=(size[1]-2*size[x])*len(fx->x) 所以

BZOJ 1827 gather 奶牛大集会

Description Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将 来参加这一次集会.当然,她会选择最方便的地点来举办这次集会.每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场.道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000).集会可以在N个农场中的任意一个举行.另

BZOJ 1827 奶牛大集会

树型DP. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 200500 #define maxe 300500 #define inf 1000000007 using namespace std; long long n,x,y,z,c[maxv],nume=0,g[maxv],val[maxv],sum=0,ans,dis[ma

[Usaco2010 Dec]Exercise 奶牛健美操

[Usaco2010 Dec]Exercise 奶牛健美操 题目 Farmer John为了保持奶牛们的健康,让可怜的奶牛们不停在牧场之间 的小路上奔跑.这些奶牛的路径集合可以被表示成一个点集和一些连接 两个顶点的双向路,使得每对点之间恰好有一条简单路径.简单的说来, 这些点的布局就是一棵树,且每条边等长,都为1. 对于给定的一个奶牛路径集合,精明的奶牛们会计算出任意点对路径的最大值, 我们称之为这个路径集合的直径.如果直径太大,奶牛们就会拒绝锻炼. Farmer John把每个点标记为1..V