您的位置:首页 > 其它

送你一颗圣诞树

2015-09-08 16:01 176 查看

题意

有mm棵树T0T_0~TmT_m,T0T_0只有一个00号节点。对于Ti(i∈[1,m])T_i(i\in[1,m]),给出ai,bi,ci,di,lia_i,b_i,c_i,d_i,l_i,表示这棵树是由TaiT_{a_i}的cic_i号节点与TbiT_{b_i}的did_i号节点间连一条长为lil_i的边构成的。在TiT_i 中,保持TaiT_{a_i} 中的所有节点编号不变,然后如果TaiT_{a_i} 中有ss 个节点,他会把TbiT_{b_i} 中的所有节点的编号加上ss。

对于Ti(i∈[1,m])T_i(i\in[1,m]),求∑i=0n−1∑j=i+1n−1d[i][j]\sum\limits_{i= 0}^{n-1}\sum\limits_{j = i + 1}^{n-1}d[i][j],其中nn为TiT_i节点个数,d[i][j]d[i][j]为该树中i,ji,j的最短距离,输出modmod 109+710^9 + 7。

0≤ai,bi<i,0≤li≤1090\le a_i,b_i < i,0\le l_i\le10^9

1≤m≤601\le m\le60

数据组数T≤100T\le 100

TimeTime Limits:2000msLimits:2000ms

MemoryMemory Limits:512MLimits:512M

分析

令ans[i]ans[i]为TiT_i的答案,size[i]size[i]为TiT_i的节点个数。

易得ans[i]=ans[ai]+ans[bi]+size[ai]∗size[bi]∗li+topai,ci∗size[bi]+topbi,di∗size[ai]ans[i] = ans[a_i] + ans[b_i]+size[a_i] * size[b_i] * l_i+top_{a_i,c_i} * size[b_i]+top_{b_i,d_i}*size[a_i]

topk,xtop_{k,x}表示TkT_k中所有节点到xx号节点的距离和。

现在我们看看如何求topk,xtop_{k,x}。

考虑把TkT_k分成Tak,TbkT_{a_k},T_{b_k}。

1° k=0k=0时,值为00;

2° x<size[ak]x < size[a_k]时,xx原来是在TakT_{a_k}中,那么显然topk,x=topak,x+(topbk,dk+(lk+disak,ck,x)∗size[bk])top_{k,x} = top_{a_k,x}+(top_{b_k,d_k}+(l_k+dis_{a_k,c_k,x})*size[b_k])

其中disk,x,ydis_{k,x,y}表示TkT_k中x,yx,y的距离。

3°x>=size[ak]x >= size[a_k]时,xx原来是在TbkT_{b_k}中,仿照2°,我们可以得出一个相似的方程。

这样就可以递归求解topk,xtop_{k,x}了。

我们的问题又变成了如何求disk,x,ydis_{k,x,y}。

同样考虑递归求解。

如果x,yx,y同属于组成TkT_k的两棵树中的一棵,我们可以递归下去;否则令x<yx,disk,x,y=disak,x,ck+disbk,y,dk+lkdis_{k,x,y} = dis_{a_k,x,c_k}+dis_{b_k,y,d_k}+l_k,这样变成了两个子问题,继续递归求解。边界为k=0或x=yk=0或x=y 时,disk,x,y=0dis_{k,x,y}=0。

上面top,distop,dis的求值过程可以用记忆化搜索。

求disk,x,ydis_{k,x,y}的过程最多递归mm层,时间复杂度为O(m)O(m);

求topk,x,ytop_{k,x,y}的过程最多有mm层,每层一个求disdis的过程,时间复杂度为O(m2)O(m^2)

所以总时间复杂度是O(m3)O(m^3)

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;

typedef long long LL;
typedef pair<LL,LL> pa;
const int N = 66;
const LL P = 1e9 + 7;
int n,a
,b
,len
;
LL size
,ans
,c
,d
;
map< pa , int > dis
;

void init() {
scanf("%d",&n);
for (int i = 1;i <= n;i ++) {
scanf("%d%d%lld%lld%d",&a[i],&b[i],&c[i],&d[i],&len[i]);
dis[i].clear();
}
}

int get(int k,LL x,LL y) {
if (k == 0 || x == y) return 0;
if (y < x) swap(x,y);
if (y < size[a[k]]) return get(a[k],x,y);
if (x >= size[a[k]]) return get(b[k],x - size[a[k]],y - size[a[k]]);
pa cur = make_pair(x,y);
if (dis[k].find(cur) != dis[k].end()) return dis[k][cur];
int re = (0LL + get(a[k],x,c[k]) + get(b[k],y - size[a[k]],d[k]) + len[k]) % P;
return dis[k][cur] = re;
}

int top(int k,LL x) {
if (k == 0) return 0;
pa cur = make_pair(x,-1);
if (dis[k].find(cur) != dis[k].end()) return dis[k][cur];
int re = 0;
if (x < size[a[k]]) {
re = top(a[k],x);
re = (re + top(b[k],d[k])) % P;
re = (re + 1LL * (get(a[k],x,c[k]) + len[k]) % P * (size[b[k]] % P) % P) % P;
}
else {
re = top(b[k],x - size[a[k]]);
re = (re + top(a[k],c[k])) % P;
re = (re + 1LL * size[a[k]] % P * (get(b[k],x - size[a[k]],d[k]) + len[k]) % P) % P;
}
return dis[k][cur] = re;
}

void solve() {
for (int i = 1;i <= n;i ++) {
int l = a[i],r = b[i];
ans[i] = (ans[l] + ans[r]) % P;
size[i] = size[l] + size[r];
ans[i] = (ans[i] + size[l] % P * (size[r] % P) % P * LL(len[i]) % P) % P;
ans[i] = (ans[i] + 1LL * size[l] % P * top(r,d[i]) % P) % P;
ans[i] = (ans[i] + 1LL * size[r] % P * top(l,c[i]) % P) % P;
printf("%lld\n",ans[i]);
}
}

int main() {
int T;
scanf("%d",&T);
size[0] = 1;
while (T --) {
init();
solve();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: