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

来源:互联网 发布:windows nt 4.0 sp6 编辑:程序博客网 时间:2024/06/15 01:43

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 longusing 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; }
原创粉丝点击