【JZOJ】4211 送你一棵圣诞树

Description

N+1 颗树,编号为 T0 ~ Tn ,对于每一棵树Ti,他在第 Tai 棵树的第 ci 个点和第 Tbi 棵树的第 di 个点之间连上了一条长度为 leni 的边。在 Ti 中,他保持 Tai 中的所有节点编号不变,把 Tbi 中的所有节点的编号加上 Sizeai
我们定义一棵树的美观度为两两点之间的距离之和,现在要输出所有树的美观度。

Brute Force

我们可以模拟这个树的构成过程,然后把他们全部存下来。
对于美观度暴力遍历整棵树,加起来。

Analysis

这题其实是很隐蔽的搜索题
仔细想想,这棵树其实是之前的两棵树的组合,我们有必要知道整棵树吗?
我们只需要知道是哪颗树就行了。
这里写图片描述
我们看当前这棵树。
我们已知他的左儿子是 Ta ,右儿子是 Tb ,连接点是 x y Suma Sumb (美观度), Sizea Sizeb (节点数),连接的边长度为蓝色那条边为 len
我们不妨再设 D[t][i] 表示第 t 棵树,编号为i的节点到整棵树的距离和。
至此,我们再结合 D[a][x] D[b][y] ,我们不难推出 Sumi
问题 Suma Sumb 是子问题,之前一定会处理过的。
那么我们剩下的问题就是 D[t][i] 怎么求了。
我们设一个状态 Dis[t][i][j] 表示树 t 里编号为i的点到编号 j 的点的距离。
我们可以通过Dis[t][i][j]来在每次合并的时候推出 D[t][i]
D[t][i] 就是现在的 D[t][i] 加上多出来的子树到其关键点的距离和加上 len 的其 Size 倍再加上自己到自己所在子树的关键点的距离的 Size 倍。
比较绕口但是也就这样了。

直接存下来肯定是不现实的。全部都转移也是不现实的。
怎么办?
观察发现,有效的点实质上很少。
我们用得上的 x y都是题目给出的。
我们把这些点叫为关键点。
你是不是已经知道怎么做了呢?
我们只存下关键点的相关状态。
什么叫相关状态呢?比如我这颗树把 x 标记为关键点,为了更新x,我们必须在之前的树里都一直维护 x 的信息,递归下去,把标记都打上,这就是这个关键点的相关状态。
我们有N棵树,每棵树最多标记 2N 个点,总共只有 NN2 个关键点
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值