[dp][递归] Jzoj P4211 送你一棵圣诞树

Description
再过三个多月就是圣诞节了,小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 取模后输出。
 
Input
第一行输入一个正整数T 表示数据组数。每组数据的第一行是一个整数m,接下来m 行每行五个整数ai, bi, ci, di, li,保证0 <= ai, bi < i, 0<= li<= 10^9,ci, di 存在。
Output
对于每组询问输出m 行。第i 行输出Ti 的权值
 
Sample Input
1
2
0 0 0 0 2
1 1 0 0 4
Sample Output
2
28
 
Data Constraint
对于30% 的数据,m <= 8
对于60% 的数据,m <= 16
对于100% 的数据,1 <= m<= 60,T<= 100

 

题解

  • 题目大意:给定了n棵树,每次会奖两棵树中的x,y之间连一条len的边,也就是将两棵树合并,问每棵树上两两点对的最短路径和
  • 设calc1(x,y)为x这棵树里,其他点到y的最短路径和,calc2(x,y,k)为x这棵树里,y到k的最短路径长度
  • 设f[i]为第i棵数的答案,f[i]=f[i.left]+f[i.right]+calc1(i.left,i.c)*i.right.size+calc1(i.right,i.d)*i.left.size+i.len*i.left.size*i.right.size
  • calc1(x,y)中可以分成两种情况,①在x的左子树②在x的右子树
  • 当在x的左子树时calc1(x,y)=calc1(x.left,y)+(calc2(y,x.left,x.c)+x.len)*x.right.size+calc1(x.right,x.d),但在右子树里也是同理
  • calc2(x,y,z)也可以想上面分成两种情况,①y和k在同一棵子树里②y和k不在同一棵子树里
  • 当在它们在同一颗子树里,直接往下递归;当它们不在同一棵子树里时,calc2(x,y,z)=calc2(x.left,y,x.c)+calc2(x.right,z,x.d)+x.len
  • 这题比较恶心,还要用map优化

代码

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<map>
 5 #define ll long long
 6 using namespace std;
 7 const ll N=70,mo=1e9+7;
 8 ll T,n,f[N];
 9 typedef pair<ll,ll> node;
10 struct edge{ ll a,b,size,c,d,v; }p[N];
11 map <node,ll> Q[N];
12 ll calc2(ll k,ll x,ll y)
13 {
14     if (x>y) swap(x,y);
15     if (k==0||x==y) return 0;
16     if (x<p[p[k].a].size&&y<p[p[k].a].size) return calc2(p[k].a,x,y);
17     else if (x>=p[p[k].a].size&&y>=p[p[k].a].size) return calc2(p[k].b,x-p[p[k].a].size,y-p[p[k].a].size);
18     else 
19     {
20         node q=make_pair(x,y);
21         if (Q[k].find(q)!=Q[k].end()) return Q[k][q];
22         return Q[k][q]=(calc2(p[k].a,p[k].c,x)+calc2(p[k].b,p[k].d,y-p[p[k].a].size)+p[k].v)%mo;
23     }
24 }
25 ll calc1(ll k,ll x)
26 {
27     ll r=0; node q=make_pair(x,0);
28     if (Q[k].find(q)!=Q[k].end()) return Q[k][q];
29     if (k==0) return 0;
30     if (x<p[p[k].a].size) r=(calc1(p[k].a,x)+calc1(p[k].b,p[k].d))%mo+(((p[k].v+calc2(p[k].a,p[k].c,x))%mo)*(p[p[k].b].size%mo))%mo;
31     else r=(calc1(p[k].a,p[k].c)+calc1(p[k].b,x-p[p[k].a].size))%mo+(((p[k].v+calc2(p[k].b,p[k].d,x-p[p[k].a].size))%mo)*(p[p[k].a].size%mo)%mo);
32     return (Q[k][q]=r)%mo;
33 }
34 int main()
35 {
36     freopen("data.in","r",stdin);
37     scanf("%d",&T);
38     while (T--)
39     {
40         scanf("%d",&n),memset(p,0,sizeof(p)),p[0].size=1;
41         for (int i=1;i<=n;i++) scanf("%lld%lld%lld%lld%lld",&p[i].a,&p[i].b,&p[i].c,&p[i].d,&p[i].v),p[i].size=p[p[i].a].size+p[p[i].b].size,Q[i].clear();
42         for (int i=1;i<=n;i++)
43             f[i]=((f[p[i].a]+f[p[i].b])%mo+(calc1(p[i].a,p[i].c)*(p[p[i].b].size%mo)+(calc1(p[i].b,p[i].d)*(p[p[i].a].size%mo))%mo
44                 +(((p[i].v%mo)*(p[p[i].a].size%mo))%mo*(p[p[i].b].size%mo))%mo)%mo)%mo,
45             printf("%lld\n",f[i]);
46     }
47 }

 

转载于:https://www.cnblogs.com/Comfortable/p/10317133.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值