统计损失 方法记录
统计损失
题目背景
无
题目描述
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;
}

浙公网安备 33010602011771号