7.3 练习赛zi

zi

【问题描述】
我们有 m+1棵树 ,分别是 T0,T1,…,Tm。其中 T0是一棵只有个点的树 ,点 的编号为 0。
生成第 i棵树我们需要五个参数 ai,bi,ci,di,li(ai,bi < i)。我们生成第 。我们生成第i棵树是 将第ai棵树的ci号点和第bi棵树的di号点用一条长度为li的边连接起来形成新的树 (不会改变原来两棵树 不会改变原来两棵树 不会改变原来两棵树 不会改变原来两棵树 )。下面我们需要对新树中的点重编号: 对于原来在 第ai棵树中的点 ,我们不会改变他的编号 ;对于原来在第 bi棵树中的点 ,我们 会将他们的编号加上第 ai棵树的点个数作为新编号 。
定义
其中 ,n为树 Ti的大小 ,vi,vj是Ti中的 点, d(vi,vj)代表这两个点的距离。现 代表这两个点的距离。现 在希望你求出 ∀1≤i≤m,F(Ti)是多少 。
【输入格式】
第一行 一个整数 m。
接下来每行五个整数 ai,bi,ci,di,li代表第i棵树的生成方式 。
【输出格式】
m行,每行一个整数 ,代表F(Ti)mod(10^9+7)的值 。
【样例输入】
3
0 2
1 0 4
2 1 0 3
【样例输出】
2
28
216
【数据规模与约定】
对于 30%的数据, 1≤m≤10。
对于 60%的数据 ,每棵 树的点数 个数 不超过 10^5。
对于 100%的数据 ,1≤m≤60。

考试的时候准备打一个30分的暴力。失败。
本来想放弃之,结果被HF逼着打,本来以为写个70分的就不错了,结果在某dalao代码的帮助下居然A了(A的过程有点坎坷,但还是很快的)

根据题解:
每一棵树都由先前的两棵树构造而来,于是可以进行递推。
所求F(Ti)F(Ti)就是TiTi中每一对点的距离和,有
F(Ti)=F(Tai)+F(Tbi)+|Tbi|∑u∈Taid(u,ci)+|Tai|∑u∈Tbid(u,di)+|Tai||Tbi|li.F(Ti)=F(Tai)+F(Tbi)+|Tbi|∑u∈Taid(u,ci)+|Tai|∑u∈Tbid(u,di)+|Tai||Tbi|li.

设A(Ti,u)A(Ti,u)表示TiTi中所有点到uu的距离和,D(Ti,u,v)D(Ti,u,v)表示TiTi中uu到vv的距离,则
F(Ti)=F(Tai)+F(Tbi)+|Tbi|A(Tai,ci)+|Tai|A(Tbi,di)+|Tai||Tbi|li.F(Ti)=F(Tai)+F(Tbi)+|Tbi|A(Tai,ci)+|Tai|A(Tbi,di)+|Tai||Tbi|li.

不失一般,设u∈Taiu∈Tai,
A(Ti,u)=A(Tai,u)+A(Tbi,di)+|Tbi|(li+D(Tai,ci,u)).A(Ti,u)=A(Tai,u)+A(Tbi,di)+|Tbi|(li+D(Tai,ci,u)).

设u,v∈Taiu,v∈Tai,
D(Ti,u,v)=D(Tai,u,v).D(Ti,u,v)=D(Tai,u,v).

设u∈Tai,v∈Tbiu∈Tai,v∈Tbi,则
D(Ti,u,v)=D(Tai,u,ci)+li+D(Tbi,di,v.)D(Ti,u,v)=D(Tai,u,ci)+li+D(Tbi,di,v.)
可以发现递推式参数中的uu、vv都必然是某一个cici或didi,共2m2m个,所以A(Ti,u)A(Ti,u)的参数最多有2m22m2个取值,D(Ti,u,v)D(Ti,u,v)的参数最多有4m34m3个取值。记忆化递推解决。

其实我并看不太懂这个。。。
所以干脆对着代码来刚这道题吧。

对于一棵新的树i,F[i]=
F[a[i]]+F[b[i]] //两棵子树内部分别的和
+l[i] * (siz[a[i]] * siz[b[i]])//新加入的这条边,a[i]中的每个点都要经过这条边到达b[i]中的每个点,所以共经过a[i]b[i],路径为l[i] (siz[a[i]] * siz[b[i]])
+siz[b[i]]*a[i]中所有点到c[i]的距离和//因为a[i]中的每个点到达b[i]都必定经过c[i],而它要到达b[i]的每一个点,所以要经过c[i] siz[b[i]]次
+siz[a[i]]*b[i]中所有点到d[i]的距离和//同上

相信现在你对如何确定F[i]的值已经有了一个初步的认识了
那我们来写代码吧!

然而还是不会


【其实看了代码还是挺好懂的】

几个注意的地方:
1.long long 这个都不算坑点了。。
2.关于mod 有的时候取了mod反而不对,要注意同余的条件
3.不算注意吧,可以当个新技能,可以用map + pair 将每棵树中每个点到一点的距离存起来,运用记忆化搜索。能大大降低时间复杂度。具体参见代码

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#define ll long long
using namespace std;

const int M = 100 + 10;
const int N = 100010;
const int mod = 1e9+7;

int m;

ll a[M],b[M],c[M],d[M],l[M];
ll siz[M];
ll f[N];
map <pair<int ,int >,long long> x;

ll getdis(int i,int u,int v){//编号i的树中 u到v的距离 
    if(i==0) return 0;
    if(u>=siz[a[i]]&&v>=siz[a[i]]){
        return getdis(b[i],u-siz[a[i]],v-siz[a[i]]);
    }
    else if(u<siz[a[i]]&&v<siz[a[i]]){
        return getdis(a[i],u,v);
    }//同一颗树中  
    else{
        if(u>=siz[a[i]]){//
            return getdis(b[i],u-siz[a[i]],d[i])+getdis(a[i],v,c[i])+l[i];
        }
        else return getdis(a[i],u,c[i])+getdis(b[i],v-siz[a[i]],d[i])+l[i];
    }
}

ll dfs(int i,int u){//i这个树中所有点到u的距离和 
    if(i==0) return 0;
    if(x[make_pair(i,u)]) return x[make_pair(i,u)];
    if(u>=siz[a[i]]){//如果i是bi中的点 
        return x[make_pair(i,u)]=dfs(b[i],u-siz[a[i]])+dfs(a[i],c[i])+siz[a[i]]*(l[i]+getdis(b[i],u-siz[a[i]],d[i]));
    }
    else{
        return x[make_pair(i,u)]=dfs(a[i],u)+dfs(b[i],d[i])+siz[b[i]]*(l[i]+getdis(a[i],u,c[i]));
    }
}

int main(){
    freopen("zi.in","r",stdin);
    freopen("zi.out","w",stdout);
    scanf("%d",&m);
    siz[0]=1,f[0]=0;
    for(int i=1;i<=m;i++){
        scanf("%I64d%I64d%I64d%I64d%I64d",&a[i],&b[i],&c[i],&d[i],&l[i]);
        f[i]=(f[a[i]]+f[b[i]])%mod;//两个子树内部 
        ll L=(((siz[a[i]]*siz[b[i]])%mod)*(l[i]%mod))%mod;//li
        f[i]=(f[i]+L)%mod;
        siz[i]=siz[a[i]]+siz[b[i]];
        ll A=dfs(a[i],c[i])%mod,B=dfs(b[i],d[i])%mod;
        A=((A%mod)*siz[b[i]])%mod;
        B=((B%mod)*siz[a[i]])%mod;
        f[i]=(f[i]%mod+A%mod+B%mod)%mod;
    }
    for(int i=1;i<=m;i++)
        printf("%I64d\n",f[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值