正题
题目链接:https://jzoj.net/senior/#main/show/3844
题目大意
一棵树,求每条路径的点权乘积之和。
解题思路
若只考虑从xxx出发往子树的路径,那么有fx=ax+ax∗∑x−>yfyf_x=a_x+a_x*\sum_{x->y}f_yfx=ax+ax∗x−>y∑fy
定义gig_igi为从iii开始的路径乘积和
但是要考虑往上又往下的,我们考虑换根法,那么我们对于每个ax∗∑x−>yfya_x*\sum_{x->y}f_yax∗∑x−>yfy相当于要求出父节点的fyf_yfy,其实就是gfa−fx∗axg_{fa}-f_x*a_xgfa−fx∗ax。但是这样我们求出来的答案会有重复,我们考虑从父节点的ffaf_{fa}ffa入手,我们只要让新枚举到的数传下去的fxf_xfx是之前累计的减去即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int XJQ=10086,N=110000;
struct node{int to,next;
}a[N*2];
int n,w[N],ls[N],tot,f[N],g[N],ans;
void addl(int x,int y)
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;
}
void dp(int x,int fa)
{for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa) continue;dp(y,x);f[x]=(f[x]+f[y])%XJQ;}f[x]=(f[x]*w[x]%XJQ+w[x])%XJQ;
}
void cr(int x,int fa,int z)
{if(x!=1)g[x]=(f[x]+z*w[x]%XJQ)%XJQ;int sum=w[x];for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa) continue;sum=(sum+f[y]*w[x]%XJQ)%XJQ;cr(y,x,(g[x]-sum+XJQ)%XJQ);}ans=(ans+g[x])%XJQ;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&w[i]),w[i]%=XJQ;for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}dp(1,1);g[1]=f[1];cr(1,1,1);printf("%d",ans);
}