jzoj 4211.【五校联考1day2】送你一颗圣诞树 分治+记忆化

Description
再过三个多月就是圣诞节了,小R想送小Y一棵圣诞树作为节日礼物。因为他想让这棵圣诞树越大越好,所以当然是买不到能够让他满意的树的,因此他打算自己把这棵树拼出来。
现在,小R开始画这棵树的设计图纸了。因为这棵树实在太大,所以他采用了一种比较方便的方法。首先他定义了 m + 1 m+1 m+1 棵树 T 0 T_0 T0 T m T_m Tm。最开始他只画好了 T 0 T_0 T0 的图纸:就只有一个点,编号为0。
接着,对于每一棵树 T i T_i Ti,他在第 T a i T_{a_i} Tai 棵树的第 c i c_i ci 个点和第 T b i T_{b_i} Tbi 棵树的第 d i d_i di 个点之间连上了一条长度为 l i li li 的边。在 T i T_i Ti 中,他保持 T a i T_{a_i} Tai 中的所有节点编号不变,然后如果 T a i T_{a_i} Tai 中有 s s s 个节点,他会把 T b i T_{b_i} Tbi 中的所有节点的编号加上 s s s
终于,他画好了所有的树。现在他定义一颗大小为 n n n的树的美观度为 ∑ i = 0 n − 1 ∑ j = i + 1 n − 1 d ( i , j ) \sum_{i=0}^{n-1}\sum_{j=i+1}^{n-1}d(i,j) i=0n1j=i+1n1d(i,j),其中 d ( i , j ) d(i,j) d(i,j) 为这棵树中 i i i j j j的最短距离。
为了方便小R 选择等究竟拼哪一棵树,你可以分别告诉他 T 1 T_1 T1 T m T_m Tm 的美观度吗?答案可能很大,请对 1 0 9 + 7 10^9 + 7 109+7 取模后输出。

Input

第一行输入一个正整数 T T T 表示数据组数。每组数据的第一行是一个整数 m m m,接下来 m m m 行每行五个整数 a i , b i , c i , d i , l i a_i, b_i, c_i, d_i, l_i ai,bi,ci,di,li,保证 0 &lt; = a i , b i &lt; i , 0 &lt; = l i &lt; = 1 0 9 , c i , d i 0 &lt;= a_i, b_i &lt; i, 0&lt;= l_i&lt;= 10^9,c_i, d_i 0<=ai,bi<i0<=li<=109ci,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 &lt; = 8 m &lt;= 8 m<=8
对于60% 的数据, m &lt; = 16 m &lt;= 16 m<=16
对于100% 的数据, 1 &lt; = m &lt; = 60 , T &lt; = 100 1 &lt;= m&lt;= 60,T&lt;= 100 1<=m<=60T<=100

分析:
假设树 C C C是由两棵树 A A A B B B合并而来,答案就是
a n s [ A ] + a n s [ B ] + s u m [ A ] ∗ s i z e [ B ] + s u m [ B ] ∗ s i z e [ A ] + s i z e [ A ] ∗ s i z e [ B ] ∗ L ans[A]+ans[B]+sum[A]*size[B]+sum[B]*size[A]+size[A]*size[B]*L ans[A]+ans[B]+sum[A]size[B]+sum[B]size[A]+size[A]size[B]L
其中 s u m [ A ] sum[A] sum[A]表示所有点到A端连接的点的距离和, s u m [ B ] sum[B] sum[B]同理, L L L为连接的边长。
s i z e size size非常好维护,考虑求距离和。
由于距离和所在树也是由两棵树合并来的,考虑分治。
x x x为A端连接的点,考虑求 s u m [ A ] sum[A] sum[A]。如果 x x x A A A树的左边的树,那么分治左边,然后加上右边树所有点到 x x x的距离。也就是右边树到右边连接点的距离和,加上右边连接点到 x x x的距离乘右边树的 s i z e size size
然后就是求树上两点的距离了,也可以考虑分治。如果两个点都在一边,直接递归这一边;否则为左边点到左边连接点,右边点到右边连接点,中间的边的和。
注意边界条件,然后开map记忆即可。
注意不能对 s i z e size size取模,因为关系到节点的编号。

代码:

#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>

const int maxn=67;
const long long mod=1e9+7;
typedef long long LL;

using namespace std;

int T,n,a,b,w;
LL x,y;
LL ans[maxn];

struct rec{
	LL x,y;
};

bool operator <(rec a,rec b)
{
	if (a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}

struct node{
	int l,r;
	int len;
	LL size,x,y;
	map <LL,LL> sum;
	map <rec,LL> dis;
}t[maxn];

LL getdis(int p,LL x,LL y)
{
	if (x==y) return 0;
	if (x>y) swap(x,y);
	LL c=t[p].dis[(rec){x,y}];
	if (c) return c;
	LL sz=t[t[p].l].size;
	if (y<=sz) c=getdis(t[p].l,x,y);
	else
	{
		if (x>sz) c=getdis(t[p].r,x-sz,y-sz);
		     else c=getdis(t[p].l,x,t[p].x)+getdis(t[p].r,y-sz,t[p].y)+(LL)t[p].len;
	}
	c%=mod;
	t[p].dis[(rec){x,y}]=c;
	return c;
}

LL getsum(int p,LL x)
{
	if (!p) return 0;
	LL c=t[p].sum[x];
	if (c) return c;
	LL sz=t[t[p].l].size;
	if (x<=sz) c=getsum(t[p].l,x)+((LL)t[p].len+getdis(t[p].l,x,t[p].x))%mod*(t[t[p].r].size%mod)%mod+getsum(t[p].r,t[p].y);
	      else c=getsum(t[p].r,x-sz)+((LL)t[p].len+getdis(t[p].r,x-sz,t[p].y))%mod*(t[t[p].l].size%mod)%mod+getsum(t[p].l,t[p].x);
	c%=mod;
	t[p].sum[x]=c;
	return c;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		t[0].size=1;
		for (int i=1;i<=n;i++)
		{
			scanf("%d%d%lld%lld%d",&a,&b,&x,&y,&w);				
			x++,y++;
			t[i].size=t[a].size+t[b].size;
			t[i].l=a,t[i].r=b;
			t[i].x=x,t[i].y=y;
			t[i].len=w;
			t[i].sum.clear();
			t[i].dis.clear();			
			ans[i]=(ans[a]+ans[b]+t[b].size%mod*getsum(a,x)%mod+t[a].size%mod*getsum(b,y)%mod+(LL)w*(t[a].size%mod)%mod*(t[b].size%mod)%mod)%mod;
			printf("%lld\n",ans[i]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值