Description
有
N+1
颗树,编号为
T0
~
Tn
,对于每一棵树Ti,他在第
Tai
棵树的第
ci
个点和第
Tbi
棵树的第
di
个点之间连上了一条长度为
leni
的边。在
Ti
中,他保持
Tai
中的所有节点编号不变,把
Tbi
中的所有节点的编号加上
Sizeai
。
我们定义一棵树的美观度为两两点之间的距离之和,现在要输出所有树的美观度。
Brute Force
我们可以模拟这个树的构成过程,然后把他们全部存下来。
对于美观度暴力遍历整棵树,加起来。
Analysis
这题其实是很隐蔽的搜索题
仔细想想,这棵树其实是之前的两棵树的组合,我们有必要知道整棵树吗?
我们只需要知道是哪颗树就行了。
我们看当前这棵树。
我们已知他的左儿子是
Ta
,右儿子是
Tb
,连接点是
x
,
我们不妨再设
D[t][i]
表示第
t
棵树,编号为
至此,我们再结合
D[a][x]
,
D[b][y]
,我们不难推出
Sumi
。
问题
Suma
,
Sumb
是子问题,之前一定会处理过的。
那么我们剩下的问题就是
D[t][i]
怎么求了。
我们设一个状态
Dis[t][i][j]
表示树
t
里编号为
我们可以通过
D[t][i]
就是现在的
D[t][i]
加上多出来的子树到其关键点的距离和加上
len
的其
Size
倍再加上自己到自己所在子树的关键点的距离的
Size
倍。
比较绕口但是也就这样了。
直接存下来肯定是不现实的。全部都转移也是不现实的。
怎么办?
观察发现,有效的点实质上很少。
我们用得上的
x
,
我们把这些点叫为关键点。
你是不是已经知道怎么做了呢?
我们只存下关键点的相关状态。
什么叫相关状态呢?比如我这颗树把
x
标记为关键点,为了更新
我们有
Dis[t][i][j]
的转移是
O(N2)
,
D[t][i]
转移是
O(N)
每棵树都要更新状态,总的时间复杂度是
O(N3)
Summary
这题非常灵活地运用了记忆化搜索的递归性质。
其实这和一些常见的最短路只存关键点状态是相似的。
只存关键点。
由于有点难理解所以我贴下代码。
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int , int> PI;
typedef long long LL;
const int N = 130 , Mo = 1e9 + 7;
PI Bz[N][N];
struct Node{
int s[2];
LL a,b,len,sum;
}Q[N];
LL Size[N],Dis[N][N][N],D[N][N];
int n,fi[N];
int Mark(int x , LL p)
{
++ fi[x];
if (x == 1) return fi[x];
if (p > Size[Q[x].s[0]]) Bz[x][fi[x]] = PI(1 , Mark(Q[x].s[1] , p - Size[Q[x].s[0]]));
else Bz[x][fi[x]] = PI(0 , Mark(Q[x].s[0] , p));
return fi[x];
}
void Solve()
{
memset(fi , 0 , sizeof fi);
scanf("%d" , &n) ; n ++;
Size[1] = 1;
for (int i = 2 ; i <= n ; i ++)
{
scanf("%d%d%I64d%I64d%I64d", &Q[i].s[0], &Q[i].s[1], &Q[i].a, &Q[i].b, &Q[i].len);
Q[i].len %= Mo;
Q[i].s[0] ++ , Q[i].s[1] ++ , Q[i].a ++ , Q[i].b ++;
Size[i] = Size[Q[i].s[0]] + Size[Q[i].s[1]]; // Size is used by MARK , we can't mod it.
Q[i].a = Mark(Q[i].s[0] , Q[i].a);
Q[i].b = Mark(Q[i].s[1] , Q[i].b);
}
for (int i = 2 ; i <= n ; i ++)
{
Size[i] %= Mo;
int lch = Q[i].s[0] , rch = Q[i].s[1] , len = Q[i].len;
for (int j = 1 ; j <= fi[i] ; j ++)
{
PI p = Bz[i][j] , p1;
for (int k = 1 ; k <= fi[i] ; k ++)
{
p1 = Bz[i][k];
if (p.first == p1.first) {Dis[i][j][k] = Dis[Q[i].s[p.first]][p.second][p1.second];continue;}
int b , b1;
b = (!p.first) ? Q[i].a : Q[i].b;
b1 = (!p1.first) ? Q[i].a : Q[i].b;
Dis[i][j][k] = ((Dis[Q[i].s[p.first]][b][p.second] + Dis[Q[i].s[p1.first]][b1][p1.second]) % Mo + len ) % Mo;
}
}
lch = Q[i].a , rch = Q[i].b;
for (int j = 1 ; j <= fi[i] ; j ++)
{
PI p = Bz[i][j];
LL dis = Dis[Q[i].s[p.first]][(p.first) ? rch : lch][p.second];
D[i][j] = D[Q[i].s[p.first]][p.second];
D[i][j] = (D[i][j] + (D[Q[i].s[!p.first]][(p.first) ? lch : rch] + Size[Q[i].s[!p.first]] * (len + dis) % Mo) % Mo) % Mo;
}
lch = Q[i].s[0], rch = Q[i].s[1];
Q[i].sum = D[lch][Q[i].a] * Size[rch] % Mo;
(Q[i].sum += D[rch][Q[i].b]* Size[lch] % Mo) %= Mo;
Q[i].sum = Q[i].sum + Size[lch] * Size[rch] % Mo * len % Mo;
Q[i].sum = (Q[i].sum + Q[lch].sum) % Mo + Q[rch].sum % Mo;
Q[i].sum %= Mo;
printf("%I64d\n" , Q[i].sum);
}
}
int main()
{
freopen("0.in" , "r" , stdin);freopen("0.out" , "w" , stdout);
int T;
scanf("%d" , &T);
while (T --) Solve();
return 0;
}