zi
【问题描述】
我们有 m+1棵树 ,分别是 T0,T1,…,Tm。其中 T0是一棵只有个点的树 ,点 的编号为 0。
生成第 i棵树我们需要五个参数 ai,bi,ci,di,li(ai,bi < i)。我们生成第 。我们生成第i棵树是 将第ai棵树的ci号点和第bi棵树的di号点用一条长度为li的边连接起来形成新的树 (不会改变原来两棵树 不会改变原来两棵树 不会改变原来两棵树 不会改变原来两棵树 )。下面我们需要对新树中的点重编号: 对于原来在 第ai棵树中的点 ,我们不会改变他的编号 ;对于原来在第 bi棵树中的点 ,我们 会将他们的编号加上第 ai棵树的点个数作为新编号 。
定义
其中 ,n为树 Ti的大小 ,vi,vj是Ti中的 点, d(vi,vj)代表这两个点的距离。现 代表这两个点的距离。现 在希望你求出 ∀1≤i≤m,F(Ti)是多少 。
【输入格式】
第一行 一个整数 m。
接下来每行五个整数 ai,bi,ci,di,li代表第i棵树的生成方式 。
【输出格式】
m行,每行一个整数 ,代表F(Ti)mod(10^9+7)的值 。
【样例输入】
3
0 2
1 0 4
2 1 0 3
【样例输出】
2
28
216
【数据规模与约定】
对于 30%的数据, 1≤m≤10。
对于 60%的数据 ,每棵 树的点数 个数 不超过 10^5。
对于 100%的数据 ,1≤m≤60。
思路:
每一棵树都由先前的两棵树构造而来,于是可以进行递推。
所求F(Ti)就是Ti中每一对点的距离和,有
F(Ti)=F(Tai)+F(Tbi)+|Tbi|∑u∈Taid(u,ci)+|Tai|∑u∈Tbid(u,di)+|Tai||Tbi|li.
设A(Ti,u)表示Ti中所有点到u的距离和,D(Ti,u,v)表示Ti中u到v的距离,则
F(Ti)=F(Tai)+F(Tbi)+|Tbi|A(Tai,ci)+|Tai|A(Tbi,di)+|Tai||Tbi|li.
不失一般,设u∈Ta,
A(Ti,u)=A(Tai,u)+A(Tbi,di)+|Tbi|(li+D(Tai,ci,u)).
设u,v∈Tai,
D(Ti,u,v)=D(Tai,u,v).
设u∈Tai,v∈Tbi,则
D(Ti,u,v)=D(Tai,u,ci)+li+D(Tbi,di,v.)
可以发现递推式参数中的u、v都必然是某一个ci或di,共2m个,所以A(Ti,u)的参数最多有2m^2个取值,D(Ti,u,v)的参数最多有4m^3个取值。记忆化递推解决。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#define N 100
#define mod 1000000007
#define LL long long
using namespace std;
LL m,f[N],siz[N],a[N],b[N],c[N],d[N],l[N];
map< pair<LL,LL>, LL > mp;
LL dis(LL x, LL u, LL v){//在编号为x的树中,求u~v的距离
if(x == 0) return 0;//T0,一个点
if(u>=siz[a[x]] && v>=siz[a[x]]){//在同一棵树,递归计算
return dis(b[x], u-siz[a[x]], v-siz[a[x]]);
}
else if(u<siz[a[x]] && v<siz[a[x]]){
return dis(a[x], u, v);
}
else{//在不同的树,跨l计算
if(u >= siz[a[x]]){
return dis(b[x], u-siz[a[x]], d[x]) + dis(a[x], v, c[x]) + l[x];
}
else{
return dis(b[x], v-siz[a[x]], d[x]) + dis(a[x], u, c[x]) + l[x];
}
}
}
LL dfs(LL x, LL pos){//x树中所有点到pos点的距离和
if(x == 0) return 0;
if( mp[make_pair(x, pos)] ) return mp[make_pair(x, pos)];//记忆化
if(pos >= siz[a[x]])
return mp[make_pair(x, pos)] =
dfs(b[x], pos-siz[a[x]])//pos在b[x]树中,对于b[x]中的点递归计算
+ dfs(a[x], c[x])//a[x]中的点要到b[x]中,先到c[x]
+ siz[a[x]] * (l[x] + dis(b[x], pos-siz[a[x]], d[x]));
//所有a[x]中的点都要过l,d[x]~pos
else
return mp[make_pair(x, pos)] = //同上
dfs(a[x],pos)
+ dfs(b[x],d[x])
+ siz[b[x]] * (l[x] + dis(a[x], pos, c[x]));
}
int main(){
freopen("zi.in","r",stdin);
freopen("zi.out","w",stdout);
cin>>m;
siz[0] = 1; f[0] = 0;//所求F(Ti)就是Ti中每一对点的距离和
for(LL i=1; i<=m; i++){
cin>>a[i]>>b[i]>>c[i]>>d[i]>>l[i];
f[i] = (f[a[i]] + f[b[i]]) % mod;//先统计a[i],b[i]内部的点对
LL cc = (siz[a[i]] % mod * siz[b[i]] % mod * l[i] % mod) % mod;
//统计l的贡献
f[i] = (f[i] % mod + cc % mod) % mod;
siz[i] = siz[a[i]] + siz[b[i]];
LL a1 = dfs(a[i], c[i]) % mod, a2 = dfs(b[i], d[i]) % mod;
a1 = (a1 * siz[b[i]] % mod) % mod;
a2 = (a2 * siz[a[i]] % mod) % mod;
f[i] = (f[i] % mod + a1 % mod + a2 % mod) % mod;
//因为a[i]中所有点都会到b[i]中所有点
//所以针对a[i]中的一个点u,b[i]中每个点都要到d[i],然后l,然后到u
//再考虑所有,b[i]中每个点都要到d[i]重复siz[a[i]]次(考虑过l了)
//每一个u都会被从c[i]到达siz[b[i]]次
//所以ans += a[i]中每个点都要到c[i]的距离和(a1)* siz[b[i]]
//所以ans += b[i]中每个点都要到d[i]的距离和(a2)* siz[a[i]]
}
for(LL i=1; i<=m; i++)
cout << f[i] << endl;
return 0;
}