统计损失 方法记录

统计损失

题目背景

题目描述

SJY 有一天被 LLT 紧急召去计算一些可能的损失。LLT 元首管理的 SHB 国的交通形成了一棵树,现在将会出现一颗陨石砸在 SHB 国中,并且陨石砸毁的必定是 SHB 国构成的交通树上的一条路径。SHB 国的损失可表示为被砸毁的路径上的所有城市价值之积。现在还暂时无法确定陨石的掉落路线,所以 LLT 元首希望 SJY 能够告诉他 SHB 国在受到每一种砸毁方式后会受到的损失之和模 \(10086\) 之后的值。

注意:单独一个节点也被认为是合法的路径

输入格式

\(1\) 行一个数 \(n\),表示城市数。

\(2\)\(n\) 个数,第 \(i\) 个数表示第 \(i\) 个城市的价值。

\(3\)\(n+1\) 行,每行两个数 \(u,v\),表示城市 \(u,v\) 之间有一条道路。

输出格式

包含一个数,表示 SHB 国将受到的损失之和。

样例 #1

样例输入 #1

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

样例输出 #1

778

提示

对于 \(20\%\) 的数据,\(n\le100\);

对于 \(50\%\) 的数据,\(n\le3000\);

对于 \(100\%\) 的数据,\(n\le100000\)

题解

读罢,暴力的方法不难想:枚举图上的每一条链,并统计答案。

实现方法也很简单。在\(dfs\)函数中我们需要记录\(4\)个元素:当前枚举到的点、终点、当前点的父节点(当下一个点为父节点时跳过,确保不走回头路)、从枚举起点开始的城市价值之积。

采用链式前向星存图。在主函数中,我们枚举每一个起点到每一个终点,这样可以得到一条链的情况。然后对于每条链都跑一边\(dfs\).

for(int i=1;i<n;i++)//枚举每一个起点 
	for(int j=i+1;j<=n;j++)//枚举每一个终点 
		dfs(i,j,i,a[i]);
void dfs(int u,int ed,int fa,int val)
{
	for(int i=head[u];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa) continue;//下一个点是父节点,跳过,不走回头路 
		if(v==ed)//遍历完整条链,记入ans 
		{
			ans+=(val*a[v])%mod;
			if(ans>=mod) ans%=mod;
		}
		dfs(v,ed,u,val*a[v]%mod);//未遍历完整条链,更新现有的城市之积 
	}
}

再考虑正解

暴力为什么是暴力?——因为它枚举了每一条链,并对每一条链都跑了\(dfs\),这是使时间复杂度丑陋的主要原因。

既然对每条链都跑\(dfs\)是无法被接受的,那便考虑跑一回\(dfs\)处理完最终答案。

空间换时间,在线转离线

下面会用到\(3\)个一维数组,分别是\(h,f,g\)

\(u\)表示当前节点,\(v\)表示当前节点的下一个节点。

\(h[i]\)表示第\(i\)个城市的价值,即输入的第\(2\)行。

\(f[u]\)表示以\(u\)为起点,所有下一步能到达的节点(父节点除外)之积。

\(g[u]\)表示以\(u\)为起点,所有\(f[v]\)的和。

如此大费周折地记录信息,是为了以一种合理的方式为答案作出贡献。

对于每一个点,\(ans\)加上\(f[v]*h[u]*g[u]\).由于“单独一个节点也被认为是合法的路径”,所以每次\(ans\)还需另外加上\(f[u]\).

以上便是核心中的核心。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
const int mod=10086;
struct Edge
{
	int u,v,nex;
}e[N<<1];
int head[N],tot;
void add(int u,int v)
{
	tot++;
	e[tot].u=u;
	e[tot].v=v;
	e[tot].nex=head[u];
	head[u]=tot;
}
int n,h[N],f[N],g[N],ans;
void dfs(int u,int fa)//dfs中只记录当前节点和当前节点的父节点即可 
{
	f[u]=h[u];
	g[u]=0;
	for(int i=head[u];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa) continue;//不走回头路 
		dfs(v,u);
		f[u]=(f[u]+1ll*f[v]*h[u])%mod;//更新f 
		ans=(ans+1ll*f[v]*h[u]*g[u])%mod;//贡献ans 
		g[u]=(g[u]+f[v])%mod;//更新g 
	}
	ans=(ans+f[u])%mod;//“单独一个节点也被认为是合法的路径” 
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&h[i]);
	for(int i=1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs(1,0);
	printf("%d\n",ans);
	return 0;
}

参考

posted @ 2022-10-15 10:30  Fish4174  阅读(36)  评论(0)    收藏  举报