Description
描述
洗完衣服,就要晒在树上。但是这个世界并没有树,我们需要重新开始造树。我们一开始拥有 $T_0$,是一棵只有一个点的树,我们要用它造出更多的树。
生成第i棵树我们需要五个参数 $a_i,b_i,c_i,d_i,l_i(a_i,b_i<i)$。我们生成第$i$棵树是将第$a_i$棵树的$c_i$号点和第$b_i$棵树的$d_i$号点用一条长度为$l_i$的边连接起来形成的新的树(不会改变原来两棵树)。下面我们需要对新树中的点重编号;对于原来在第$a_i$棵树中的点,我们不会改变他们的编号;对于原来在第$b_i$棵树中的点,我们会将他们的编号加上第$a_i$棵树的点的个数作为新的编号。
定义
$$ F(T_i) = \sum_{i=0}^{n-1} \sum_{j=i+1}^{n-1} d(v_i, v_j)$$
其中,$n$为树$T_i$的大小,$v_i,v_j$是$T_i$中的点,$d(v_i,v_j)$代表这两个点的距离。现在希望你求出$∀1≤i≤m,F(T_i)$是多少。输入
第一行一个整数$m$,代表要造多少棵树。
接下来$m$行,每行$5$个数$a_i,b_i,c_i,d_i,l_i$。
输出
$m$行每行一个整数代表$F(T_i)$对$10^9+7$取模之后的值。
样例
输入
3
0 0 0 0 2
1 1 0 0 4
2 2 1 0 3输出
2
28
216约定
对于$30\%$的数据,$1≤m≤10$。
对于$60\%$的数据,每棵树的点数个数不超过$10^5$。
对于$100\%$的数据,$1≤m≤60$。
Solution
让我们用$ans_i$表示第$i$次操作的答案,不难想到$ans_i = ans_{a_i} + ans_{b_i} + \dots$
那么后面还要加什么呢?
举个例子
红色的为$a$,蓝色的为$b$,橙色的为这次连接的长度为$l$的边
既然两棵子树内部的答案都已经统计了,我们只需要统计跨$l$的答案即可
首先考虑$l$的贡献,因为$a$和$b$中的每一个节点都会记录一次答案,所以$l$经过了$size_a \times size_b$次
然后,我们显然可以用$\sum_{i=0}^{size_a - 1} \sum_{j = 0}^{size_b - 1} ( \operatorname{dist}(i, c) + \operatorname{dist}(j, d)) $,来表示剩余的答案,化简一下:
$\sum_{i=0}^{size_a - 1} \sum_{j = 0}^{size_b - 1} ( \operatorname{dist}(i, c) + \operatorname{dist}(j, d)) $
$= \sum_{i=0}^{size_a - 1} (\sum_{j = 0}^{size_b - 1} \operatorname{dist}(i, c) + \operatorname{g}(b, d))$
$= \sum_{i=0}^{size_a - 1} \sum_{j = 0}^{size_b - 1} \operatorname{dist}(i, c) + \sum_{i=0}^{size_a - 1}\operatorname{g}(b, d)$
$= \sum_{j = 0}^{size_b - 1} \operatorname{g}(a, c) + \sum_{i=0}^{size_a - 1}\operatorname{g}(b, d)$
$= \operatorname{g}(a, c) \times size_b + \operatorname{g}(b, d) \times size_a$
其中$\operatorname{g}(T, u)$表示在$T$这棵树中,$u$这个节点到其它所有节点的距离和
于是就可以愉快的计算答案啦
int main()
{
ios::sync_with_stdio(false);
cin >> m;
for(register int i = 1; i <= m; i++)
{
cin >> a[i] >> b[i] >> c[i] >> d[i] >> l[i];
}
size[0] = 1;
for(register int i = 1; i <= m; i++)
{
// 先递推 size[]
size[i] = (size[a[i]] + size[b[i]]) % MOD;
}
for(register int i = 1; i <= m; i++)
{
// 计算 ans[] 并输出
ans[i] = ((ans[a[i]] + ans[b[i]]) % MOD + size[a[i]] * size[b[i]] % MOD * l[i] % MOD) % MOD;
ans[i] = (ans[i] + g(a[i], c[i]) * size[b[i]] % MOD + g(b[i], d[i]) * size[a[i]] % MOD) % MOD;
cout << ans[i] << endl;
}
return 0;
}
怎么实现$\operatorname{g}()$呢?
递推当然是可以的,但必然需要$\mathcal{O}(n \times m)$的复杂度,而$n$却高达$2^{60}$,一种可行的方法是使用递归,只计算需要的,再加上记忆化,就变成$\log$了
再用刚才的例子
现在我们想要计算$\operatorname{g}(^* this, c)$
其答案显然为:
是不是就等于$\operatorname{g}(a, c) + \operatorname{g}(b, d) + l \times size_b$?
那要是计算$\operatorname{g}(^* this, u)$呢?
要是按照刚刚的方法,计算$\operatorname{g}(a, c) + \operatorname{g}(b, d) + l \times size_b$的话:
漏掉的这个是什么?恰好就是$size_b \times \operatorname{dist}(a, u, c)$(这里的$ \operatorname{dist}$是指在$a$这棵子树中,$u$和$c$的距离)
于是
$$ \operatorname{g}(^* this, u) = \operatorname{g}(a, c) + \operatorname{g}(b, d) + (l + \operatorname{dist}(a, u, c)) \times size_b $$
$u$在$b$中同理,注意$u$的编号要减去$size_a$
LL g(LL id, LL pos)
{
// map1 是记忆化映射
if(!id) return 0;
pair<LL, LL> query = make_pair(id, pos);
if(map1.count(query)) return map1[query]; // 调用老数据
LL npos = pos - size[a[id]];
// 计算并记忆化
if(pos < size[a[id]])
map1[query] = ((dist(a[id], c[id], pos) + l[id]) * size[b[id]] % MOD + g(b[id], d[id]) + g(a[id], pos)) % MOD;
else
map1[query] = ((dist(b[id], d[id], npos) + l[id]) * size[a[id]] % MOD + g(a[id], c[id]) + g(b[id], npos)) % MOD;
return map1[query];
}
问题又来了,$\operatorname{dist}(T, u, v)$怎么算呢?
LCA?建树也需要时间
考虑到我们拥有完整的建树过程,而且树的种类至多$60$种,那么当然可以递归来求啦
也就是分类讨论一下
- 如果$u, v$在$T$的同一个子树$child$中,显然$\operatorname{dist}(T, u, v) = \operatorname{dist}(child, u, v)$
- 否则,就是两者到$l$的距离和加上$l$的长度,$\operatorname{dist}(T, u, v) = \operatorname{dist}(child1, u, c) + \operatorname{dist}(child2, v, d) + l$
同样注意$b$那棵树里的节点编号要减去$size_a$
LL dist(LL id, LL pos1, LL pos2)
{
if(!id || pos1 == pos2) return 0;
pair<LL, pair<LL, LL> > query = make_pair(id, make_pair(pos1, pos2));
if(map2.count(query)) return map2[query];
if(pos1 < size[a[id]]) // pos1 在 a 中
{
if(pos2 < size[a[id]]) map2[query] = dist(a[id], pos1, pos2); // pos2 也在 a 中
else map2[query] = (dist(a[id], pos1, c[id]) + dist(b[id], pos2 - size[a[id]], d[id]) + l[id]) % MOD; // pos2 在 b 中
}
else // pos1 在 b 中
{
if(pos2 < size[a[id]]) map2[query] = (dist(a[id], pos2, c[id]) + dist(b[id], pos1 - size[a[id]], d[id]) + l[id]) % MOD; // pos2 在 a 中
else map2[query] = dist(b[id], pos1 - size[a[id]], pos2 - size[a[id]]); // pos2 在 b 中
}
return map2[query];
}
上述两个函数的边界都非常显然,于是就愉快的解决了这道题
完整代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD = 1000000007LL;
const int N = 70;
map<pair<LL, LL>, LL> map1;
map<pair<LL, pair<LL, LL> >, LL> map2;
LL m, a[N], b[N], c[N], d[N], l[N], ans[N], size[N];
LL dist(LL id, LL pos1, LL pos2)
{
if(!id || pos1 == pos2) return 0;
pair<LL, pair<LL, LL> > query = make_pair(id, make_pair(pos1, pos2));
if(map2.count(query)) return map2[query];
if(pos1 < size[a[id]]) // pos1 在 a 中
{
if(pos2 < size[a[id]]) map2[query] = dist(a[id], pos1, pos2); // pos2 也在 a 中
else map2[query] = (dist(a[id], pos1, c[id]) + dist(b[id], pos2 - size[a[id]], d[id]) + l[id]) % MOD; // pos2 在 b 中
}
else // pos1 在 b 中
{
if(pos2 < size[a[id]]) map2[query] = (dist(a[id], pos2, c[id]) + dist(b[id], pos1 - size[a[id]], d[id]) + l[id]) % MOD; // pos2 在 a 中
else map2[query] = dist(b[id], pos1 - size[a[id]], pos2 - size[a[id]]); // pos2 在 b 中
}
return map2[query];
}
LL g(LL id, LL pos)
{
// map1 是记忆化映射
if(!id) return 0;
pair<LL, LL> query = make_pair(id, pos);
if(map1.count(query)) return map1[query]; // 调用老数据
LL npos = pos - size[a[id]];
// 计算并记忆化
if(pos < size[a[id]])
map1[query] = ((dist(a[id], c[id], pos) + l[id]) * size[b[id]] % MOD + g(b[id], d[id]) + g(a[id], pos)) % MOD;
else
map1[query] = ((dist(b[id], d[id], npos) + l[id]) * size[a[id]] % MOD + g(a[id], c[id]) + g(b[id], npos)) % MOD;
return map1[query];
}
int main()
{
ios::sync_with_stdio(false);
cin >> m;
for(register int i = 1; i <= m; i++)
{
cin >> a[i] >> b[i] >> c[i] >> d[i] >> l[i];
}
size[0] = 1;
for(register int i = 1; i <= m; i++)
{
// 先递推 size[]
size[i] = (size[a[i]] + size[b[i]]) % MOD;
}
for(register int i = 1; i <= m; i++)
{
// 计算 ans[] 并输出
ans[i] = ((ans[a[i]] + ans[b[i]]) % MOD + size[a[i]] * size[b[i]] % MOD * l[i] % MOD) % MOD;
ans[i] = (ans[i] + g(a[i], c[i]) * size[b[i]] % MOD + g(b[i], d[i]) * size[a[i]] % MOD) % MOD;
cout << ans[i] << endl;
}
return 0;
}