【五校联考1day2】送你一颗圣诞树

本文介绍了一种计算特殊构造的圣诞树美观度的方法。通过合并两棵树并连接特定节点,形成更大的树。美观度被定义为树中任意两点间最短路径之和。文章详细解释了如何使用记忆化搜索来高效计算每棵树的美观度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

再过三个多月就是圣诞节了,小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]);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值