题目
再过三个多月就是圣诞节了,小R 想送小Y 一棵圣诞树作为节日礼物。因为他想让这棵圣诞树越大越好,所以当然是买不到能够让他满意的树的,因此他打算自己把这棵树拼出来。
现在,小R 开始画这棵树的设计图纸了。因为这棵树实在太大,所以他采用了一种比较方便的方法。首先他定义了m+ 1 棵树T0 到Tm。最开始他只画好了T0 的图纸:就只有一个点,编号为0。
接着,对于每一棵树Ti,他在第Tai 棵树的第ci 个点和第Tbi 棵树的第di 个点之间连上了一条长度为li 的边。在Ti 中,他保持Tai 中的所有节点编号不变,然后如果Tai 中有s 个节点,他会把Tbi 中的所有节点的编号加上s。
终于,他画好了所有的树。现在他定义一颗大小为n 的树的美观度为,其中d(i; j) 为这棵树中i 到j 的最短距离。
为了方便小R 选择等究竟拼哪一棵树,你可以分别告诉他T1 到Tm 的美观度吗?答案可能很大,请对10^9 + 7 取模后输出。
Data Constraint
对于30% 的数据,m <= 8
对于60% 的数据,m <= 16
对于100% 的数据,1 <= m<= 60,T<= 100
分析
首先我们需要先清楚题意是什么,题意:给出n个询问,每次询问要求合并两棵树
两棵树中的x,y连接一条长度为len的边,且y子树的每个节点的编号要加上x的节点数
问每次合并后的美观度。
美观度定义为每个节点之间的最短路径,在树上此路径唯一。
现在我们先分析一下数据,m虽然<=60,但是每次合并,相当于是将节点数*2.
所以这样的点数是260明显普通的搜索是行不通的。
我们考虑一下其他思路。
这题的操作是合并,那么我们是否能根据这一点每次合并答案,而不是重新搜一次呢?
我们往这个方面想,而且合并后,我们只需在以前的两棵树的答案再加上附加的答案,即:
我们设all(v,x)表示v这颗树里面的所有其他的节点到编号为x的点的路径和。
设xtoy(v,x,y)表示在v这棵树中x到y的路径是多少。
注意:这里强调的是这棵树v中的节点,假如在v的父亲树u,
那么再假设x为右子树的点,那么在u里面,x就叫u.left.size+x,所以要注意。
当x在v的左子树里面时:
all(v,x)=all(v.left,x)+(xtoy(x,v.left.c)+v.len)*v.right.size+all(v.right,v.d);
右边的就自己推吧。
当x,y同一棵子树时,我们直接递归下去,
直到它们不同时就xtoy(v,x,y)=xtoy(v.left,x,c)+xtoy(v.right,y,d)+v.len;
好了,定义完了。
那么每次的答案就是f[v]=f[v.left]+f[v.right]+all(v.left,v.c)+all(v.right,v.d)+v.len*v.right.size *v.left.size
这是由附加的和左右子树的。
还有至于上面的公式为什么,大家请脑补一下。。
好了,我们现在考虑时间复杂度,为什么这样的速度很快。
我们设一棵树已经被做过了,那么它的子树的值一定也算过了。
好,一次合并的值我们可以先得到子树的一部分,另一部分我们只会做一次,也就是说我们每次做的都是一些小的,这样合起来成大的,而我们再加一个记忆化,map优化
就可以解决这个问题了。
即每次都可以分解,即log(tree.size=2^60)=O(n)=60
可以分成两类询问:第一类用 a, b, c 描述,表示询问第 a 棵树中第 b 个点和第 c 个点的距离;第二种用 a, b 描述,表示询问第 a 棵树中所有点到第 b 个点的距离和。可以发现如果可以得到这两类询问的答案,就可以递推出所有树的权值。
首先考虑第一类询问,如果用记忆搜的方式转化成子问题,可以发现这个搜索过程很类似
线段树:中间一部分都已经记忆搜得到过了答案,只有两侧需要继续递归下去。显然这时最多
递归 n 重,时间复杂度是 O(n)
然后考虑第二类询问,可以发现一个第二类询问可以转化为一个第一类询问和一个规模更
小的第二类询问,即至多转化为 O(n) 个第一类询问,所以时间复杂度是 O(n^2)。
因为求一棵树的权值我们需要调用一次第二类询问,所以时间复杂度是 O(n^3)
by jiry_2
这题比较复杂,请大家脑补一下。。
这次又学到了一些新东西,如map,pair,和不断往下传递不断分解的同时用记忆化来做一些复杂的合并问题。
#include<iostream>
#include<map>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=65;
const ll mo=1000000007;
ll t,n;
typedef pair<ll,ll> note;
struct node{
ll l,r,size,c,d,len;
}f[N];
ll g[N];
map <note,ll> h[N];
ll xtoy(ll v,ll x,ll y){
if (x>y) swap(x,y);
if (v==0||x==y) return 0;
if (x<f[f[v].l].size&&y<f[f[v].l].size) return xtoy(f[v].l,x,y);
else if (x>=f[f[v].l].size&&y>=f[f[v].l].size)
return xtoy(f[v].r,x-f[f[v].l].size,y-f[f[v].l].size);
else{
note u=make_pair(x,y);
if (h[v].find(u)!=h[v].end()) return h[v][u];
h[v][u]=(xtoy(f[v].l,f[v].c,x)+xtoy(f[v].r,f[v].d,y-f[f[v].l].size)+f[v].len)%mo;
return h[v][u];
}
}
ll all(ll v,ll x){
ll ans=0;
note u=make_pair(x,0);
if (h[v].find(u)!=h[v].end()) return h[v][u];
if (v==0) return 0;
if (x<f[f[v].l].size){
ans=(all(f[v].r,f[v].d)+all(f[v].l,x))%mo;
ans=ans+(((f[v].len+xtoy(f[v].l,f[v].c,x))%mo)*(f[f[v].r].size%mo))%mo;
}else {
ans=(all(f[v].l,f[v].c)+all(f[v].r,x-f[f[v].l].size))%mo;
ans=ans+(((f[v].len+xtoy(f[v].r,f[v].d,x-f[f[v].l].size))%mo)*(f[f[v].l].size%mo))%mo;
}
h[v][u]=ans;
return ans%mo;
}
int main(){
scanf("%d",&t);
for(;t;t--){
scanf("%d",&n);
memset(f,0,sizeof(f));
f[0].size=1;
for(int i=1;i<=n;i++){
h[i].clear();
scanf("%lld %lld %lld %lld %lld",&f[i].l,&f[i].r,&f[i].c,&f[i].d,&f[i].len);
f[i].size=f[f[i].l].size+f[f[i].r].size;
}
for(int i=1;i<=n;i++){
g[i]=(g[f[i].l]+g[f[i].r])%mo+(all(f[i].l,f[i].c)*(f[f[i].r].size%mo))%mo;
g[i]=g[i]+(all(f[i].r,f[i].d)*(f[f[i].l].size%mo))%mo;g[i]=g[i]%mo;
g[i]=g[i]+(((f[i].len%mo)*(f[f[i].l].size%mo))%mo*(f[f[i].r].size%mo))%mo;
g[i]=g[i]%mo;
printf("%lld\n",g[i]);
}
}
}