景点中心 【蒟蒻的小题解】

Description

话说宁波市的中小学生在镇海中学参加计算机程序设计比赛,比赛之余,他们在镇海中学的各个景点参观。镇海中学共有n个景点,每个景点均有若干学生正在参观。这n个景点以自然数1至n编号,每两个景点的编号均不同。每两个景点之间有且只有一条路径。选择哪个景点集中的学生,才能使所有学生走过的路径之和最小呢?
如果存在多个解,则输出距离1最近的那个点,可以证明答案唯一

Input

输入文件center.in中有若干行:
第一行只有一个正整数n,表示景点数。
第二行有n个1至1000间的整数,这n个整数间互相以一个空格分隔。其中第i个整数表示第i个景点处的学生数。
第三行至第n+1行,每行有三个整数I,j,k,表示景点i和景点j之间有一条长尾k的路径直接连接。其中i<>j,1≤i≤n,1≤j≤n;1≤k≤1000。

Output

输出文件center.out中有二行;
第一行只有一个整数i,表示在第i个景点处集中时,所有学生走过的路径之和最短。
第二行也只有一个整数,表示所有学生走过的路径之和的最小值。

Sample Input

4
3 2 4 1
1 2 5
3 1 6
2 4 4

Sample Output

1
43

Hint

【数据限制】
所有的数据均随机生成,且满足:
30%的数据,1≤n≤200。
60%的数据,1≤n≤3000。
100%的数据,1≤n≤100000。

分析

本题一看就是树形DP了吧。。。
好的,考虑怎么DP。
我们记 f [ x ] f[x] f[x]为他所有子节点到他的距离。
然后记 f 1 [ x ] f1[x] f1[x]表示他到根节点的距离。
最后记 n u m [ i ] num[i] num[i]表示他子节点的数量。
先用一次DFS预处理出三个值,然后考虑DP。

a n s [ s t ] = ∑ ( f [ v ] − ( f [ x ] + n u m [ x ] ∗ ( f 1 [ x ] − f 1 [ v ] ) ) ) + ( ( n u m [ v ] − n u m [ x ] ) ∗ ( f 1 [ s t ] − f 1 [ v ] ) ) ; ans[st]=\sum(f[v]-(f[x]+num[x]*(f1[x]-f1[v])))+((num[v]-num[x])*(f1[st]-f1[v])); ans[st]=(f[v](f[x]+num[x](f1[x]f1[v])))+((num[v]num[x])(f1[st]f1[v]));
结合代码理解:`

long long dp(int x)
{
	long long s=0;
	int st=x;
	while (fa[x]>0)
	{
		int v=fa[x];
		s+=1ll*(f[v]-(f[x]+1ll*num[x]*(f1[x]-f1[v])))+(1ll*(num[v]-num[x])*(f1[st]-f1[v]));
		x=v;
	}
	return s+f[st];
}

看不懂的自己YY一下吧,手模一下就懂了。

最后的答案便为: m i n ( a n s [ i ] + f [ i ] ) min(ans[i]+f[i]) min(ans[i]+f[i])了。

CODE

最后放出完整的code。

//L.E.M.T专用水印啊!!!!!!!!!!!!!!!!!!!
#include//万能头NB!
using namespace std;
int tot,head[200005],len[200005],next[200005],g[200005],a[100005],n,fa[100005];
long long num[100005],f1[100005],f[100005];
int read()//快读
{
	char ch=getchar();
	while (ch<'0'||ch>'9')
	{
		ch=getchar();
	}
	int s=0;
	while (ch>='0'&&ch<='9')
	{
		s=s*10+(ch-'0');
		ch=getchar();
	}
	return s;
}
void add(int x,int y,int z)
{
	tot++,head[tot]=y,len[tot]=z,next[tot]=g[x],g[x]=tot;
}
void dg(int x)
{
	num[x]=1ll*a[x];
	for (register int i=g[x];i;i=next[i])
	{
		int v=head[i];
		if (v!=fa[x])
		{
			f1[v]=f1[x]+1ll*len[i];
			fa[v]=x;//记录父亲
			dg(v);
			num[x]+=num[v];
			f[x]+=f[v]+num[v]*1ll*len[i];
		}
	}
}
long long dp(int x)
{
	long long s=0;
	int st=x;
	while (fa[x]>0)
	{
		int v=fa[x];
		s+=1ll*(f[v]-(f[x]+1ll*num[x]*(f1[x]-f1[v])))+(1ll*(num[v]-num[x])*(f1[st]-f1[v]));
		x=v;
	}
	return s+f[st];
}
int main()
{
	n=read();
	for (register int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	for (register int i=1;i<=n-1;i++)
	{
		int x,y,z;
		x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	dg(1);
	long long ans=0x3f3f3f3f3f3f3f;
	int t=0;
	for (register int i=1;i<=n;i++)
	{
		long long sum=dp(i);
		if (sum<ans)
		{
			ans=sum;
			t=i;
		}
	}
	printf("%d\n%lld",t,ans);
}

时间复杂度: O ( N l o g N ) O(N logN ) O(NlogN)

好的,收。

你可能感兴趣的:(树形DP,题解)