练习赛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; }
- 练习赛zi树上距离(分治)
- 树上最远距离练习
- 7.3 练习赛zi
- 树上分治
- 树上距离
- 【POJ 1741】Tree (树上点分治)
- gdfzoj #510 树上路径(点分治)
- HDU 5469(2015 ICPC上海网络赛)---Antonidas(树上分治)
- 【树上的分治】poj1741
- poj1741 树上点分治
- POJ1741Tree(树上点分治)
- 树上的分治算法
- 树上点分治入门
- HDU 4812 D Tree (树上点分治)
- POJ1741——Tree(树上分治,树的重心)
- poj1741:Tree (树上点分治/treap+启发式合并)
- bzoj 1316: 树上的询问 (点分治)
- [BZOJ1316]树上的询问(点分治+二分)
- 【Java面试】---装箱拆箱问题汇总
- python-II
- Android 通讯录(2)-----自定义View实现右侧导航栏
- HDU1754[I HATE IT] 线段树
- UDP通信类,监听本机指定端口,发送消息给指定电脑
- 练习赛zi树上距离(分治)
- 通过反射来创建对象,newInstance()和getConstructor()
- WebApi 断点上传
- linux中关闭正在运行的脚本
- IIPP Week 3
- 【容斥原理】
- JavaBeen类的几种写法及优缺点
- vue.js实例todoList
- 图像数据每行对齐到4字节