练习赛zi树上距离(分治)

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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值