CH Round#55 LCA的统计

来源:互联网 发布:一对一聊天的软件 编辑:程序博客网 时间:2024/04/30 00:14

LCA 的统计

【题目描述】

萌蛋有一棵n个节点的有根树,其根节点为1。除此之外,节点i的父节点为p[i]。每个点上都有一个权值,节点i的权值是

w[i]。萌蛋知道你一定知道什么叫做祖先(从根到某个点的路径上

的每个点都是这个点的祖先,包括它本身),也一定知道什么叫做最近公共祖先(两个点的最近公共祖先是某个点,这个点同时是两个点的祖先,且离根最远)。
   现在给出这棵树,你需要求出:


∑(1≤i≤n)∑(1≤j≤n)w[i]*w[j]*w[LCA(i,j)]


其中LCA(i,j)表示点i与点j的最近公共祖先。

由于答案可能很大,你只需要输出它对1,000,000,007取模的结果。

【输入格式】

第一行为两个整数n w[1]
第二行到第
n行,第i行有两个整数p[i],w[i]

【输出格式】

输出只有一行,为一个整数,表示所求答案对1,000,000,007取模的结果。

【样例输入】

22

11

【样例输出】

17

【数据范围】

对于 30%的数据,n≤100,w[i]≤10。
对于 60%的数据,n≤1,000,w[i]≤1,000。对于 100%的数据,1≤n≤100,000,0≤w[i]≤

1,000,000,000,1<p[i]<i。 




用sum[i]表示以i为根的子树的权值和(包括i本身),sq_sum[i]表示以i为根的子树的sum的平方和,f[i]表示以i为LCA的点的权值乘积的和。

则f[i]=w[i]*w[i]+2*((sum[i]-w[i])^2-sq_sum[i])+2*w[i]*(sum[i]-w[i])。

而sum[i]和sq_sum[i]可以通过dfs预处理,最后答案ans=∑f[i],注意在每步取模操作时注意是否会超过long long范围。




#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define F(i,j,n) for(int i=j;i<=n;i++)#define D(i,j,n) for(int i=j;i>=n;i--)#define LL long long#define MAXN 100005#define MOD 1000000007using namespace std;struct edge_type{int next,to;}e[MAXN];int n,fa,cnt=0;LL w[MAXN],sum[MAXN],sqsum[MAXN],head[MAXN],ans=0;void add_edge(int u,int v){e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;}void dfs(int k){sum[k]=w[k];sqsum[k]=0;for(int p=head[k];p;p=e[p].next){int x=e[p].to;dfs(x);sum[k]=(sum[k]+sum[x])%MOD;sqsum[k]=(sqsum[k]+sum[x]*sum[x])%MOD;}}int main(){freopen("A.in","r",stdin);freopen("A.out","w",stdout);memset(head,0,sizeof(head));scanf("%d%lld",&n,&w[1]);F(i,2,n){scanf("%d%lld",&fa,&w[i]);add_edge(fa,i);}dfs(1);F(i,1,n){LL tmp=((sum[i]-w[i])*(sum[i]-w[i])-sqsum[i]+2*w[i]*(sum[i]-w[i])+w[i]*w[i])%MOD;ans=(ans+w[i]*tmp)%MOD;}printf("%lld\n",ans);}



0 0
原创粉丝点击