题目
题目
输入/输出例子
输入
9 5
2 3 6
6 1 4
5 2 10
2 4 1
9 1 9
2 8 3
1 2 3
7 4 8
4 9
7 3
6 1
9 7
2 1
输出
1050
1054
970
1148
896
提示
无
解题思路
思路
树形DP。
分析
我们先把前 n n n 个点两两之间距离求出来。这一步可以转换为求路径,及每条边经过的次数 × \times × 边权的和。
对于第 n + 1 n+1 n+1 个点,我们发现其和前 n n n 个点的距离之和可以转换为两个东西:剩下 n − 1 n-1 n−1 个点到连接点的距离之和和新连接的边的经过次数。
定义 f [ i ] f[i] f[i] 表示原来的树中剩下 n − 1 n-1 n−1 个节点到 i i i 号节点的距离之和。
那么在第一次 dfs 中, f [ 1 ] f[1] f[1] 是可以算出来的。
在第二次 dfs 中而前 n n n 个点两两之间距离之和也可以求出来。
对于剩下的 f [ i ] f[i] f[i],我们发现,对于一个子节点 v v v,若它的父亲节点是 u u u,那么可以得出 f [ v ] = f [ u ] + w × ( n − 2 × s z [ v ] ) f[v]=f[u]+w\times(n-2\times sz[v]) f[v]=f[u]+w×(n−2×sz[v]),其中 w w w 表示连接点 u u u 和点 v v v 的边的边权, s z [ v ] sz[v] sz[v] 表示以 v v v 为根节点的子树有几个节点。
算出所有的 f [ i ] f[i] f[i] 后,对于每次的询问,可以 O ( 1 ) O(1) O(1) 得出答案。
Code
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
const int N=1e6+1;
int n,m,head[N],sz[N],x,y,z,idx,ans,d[N],up[N],f[N],g[N];
struct fy
{
int v,w,next;
}edge[N<<1];
void add(int x,int y,int z)
{
edge[++idx].v=y,edge[idx].w=z,edge[idx].next=head[x],head[x]=idx;
}
void dfs(int u,int fa)
{
sz[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].v,w=edge[i].w;
if(v==fa)
continue;
dfs(v,u);
sz[u]+=sz[v];
sz[u]%=mod;
d[u]+=w*sz[v];
d[u]+=d[v];
d[u]%=mod;
}
}
void dfs_(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].v,w=edge[i].w;
if(v==fa)
continue;
dfs_(v,u);
ans+=(sz[v]*(n-sz[v]))*w;
ans%=mod;
}
}
void dfs__(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].v,w=edge[i].w;
if(v==fa)
continue;
f[v]=f[u]+w*(n-2*sz[v]);
f[v]%=mod;
f[v]=(f[v]+mod)%mod;//注意一定要加上mod
dfs__(v,u);
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1,0);
dfs_(1,0);
f[1]=d[1];
dfs__(1,0);
//初始化f[i]
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",(ans+f[x]+n*y%mod)*2%mod);
//要乘2因为我们算的只是有序数对。
}
}