JZOJ 4211. 【五校联考1day2】送你一颗圣诞树(分治)

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

题目

Description

再过三个多月就是圣诞节了,小R想送小Y一棵圣诞树作为节日礼物。因为他想让这棵圣诞树越大越好,所以当然是买不到能够让他满意的树的,因此他打算自己把这棵树拼出来。

现在,小R开始画这棵树的设计图纸了。因为这棵树实在太大,所以他采用了一种比较方便的方法。首先他定义了 m + 1 m+ 1 m+1棵树 T 0 T0 T0 T m Tm Tm。最开始他只画好了 T 0 T0 T0 的图纸:就只有一个点,编号为0。

接着,对于每一棵树 T i Ti Ti,他在 T a i Tai Tai的第 c i ci ci个点和 T b i Tbi Tbi的第 d i di di个点之间连上了一条长度为 l i li li的边。在 T i Ti Ti中,他保持 T a i Tai Tai中的所有节点编号不变,然后如果 T a i Tai Tai中有 s s s个节点,他会把 T b i Tbi Tbi中的所有节点的编号加上 s s s

终于,他画好了所有的树。现在他定义一颗大小为n 的树的美观度为在这里插入图片描述其中 d ( i , j ) d(i,j) d(i,j) 为这棵树中i 到j 的最短距离。

为了方便小R选择等究竟拼哪一棵树,你可以分别告诉他 T 1 T1 T1 T m Tm 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 ai, bi, ci, di, li ai,bi,ci,di,li,保证 0 ≤ a i , b i < i 0≤ai, bi < i 0ai,bi<i 0 ≤ l i ≤ 1 0 9 0≤li≤10^9 0li109 c i , d i ci, di ci,di存在。

Output

对于每组询问输出 m m m行。第 i i i行输出 T i Ti Ti的权值

Sample Input

1
2
0 0 0 0 2
1 1 0 0 4

Sample Output

2
28

Data Constraint

对于30%的数据, m ≤ 8 m≤8 m8
对于60% 的数据, m ≤ 16 m≤16 m16
对于100% 的数据, 1 ≤ m ≤ 60 1≤m≤60 1m60 T ≤ 100 T≤100 T100

题解

  • 点数到最后可能会有 2 60 2^{60} 260个,会很大,不可能每个都存下来。

  • 只考虑怎么计算要求的答案就好。

  • 对于每一棵(题目写的是“颗”)树 i i i,记录 e [ i ] . a , e [ i ] . b , e [ i ] . c , e [ i ] . d , e [ i ] . l e[i].a,e[i].b,e[i].c,e[i].d,e[i].l e[i].ae[i].be[i].ce[i].de[i].l如题目中的 a , b , c , d , l a,b,c,d,l abcdl,记录 e [ i ] . s e[i].s e[i].s表示树的大小。为了方便计算,把节点编号统一 + 1 +1 +1

  • 易得 e [ i ] . s = e [ e [ i ] . a ] . s + e [ e [ i ] . b ] . s e[i].s=e[e[i].a].s+e[e[i].b].s e[i].s=e[e[i].a].s+e[e[i].b].s.

  • 定义函数 a l l ( T , x ) all(T,x) all(T,x)表示 T T T树上所有节点到 x x x节点的距离和(其中 x x x是在 T T T树中的编号)。

  • 定义函数 t o ( T , x , y ) to(T,x,y) to(T,x,y)表示 T T T树上节点 x x x到节点 y y y的距离(其中 x , y x,y x,y都是 T T T树中的编号)。

  • 发现只有这两个函数,就可以计算出答案,而且这两个函数可以通过自己不断类似分治地求答案。

  • 部分式子如下:

  • 先令 a = e [ i ] . a , b = e [ i ] . b , c = e [ i ] . c , d = e [ i ] . d a=e[i].a,b=e[i].b,c=e[i].c,d=e[i].d a=e[i].ab=e[i].bc=e[i].cd=e[i].d

  • 那么 a n s [ i ] = a n s [ a ] + a n s [ b ] + e [ a ] . s ∗ e [ b ] . s ∗ e [ i ] . l + e [ a ] . s ∗ a l l ( b , d ) + e [ b ] . s ∗ a l l ( a , c ) ans[i]=ans[a]+ans[b]+e[a].s*e[b].s*e[i].l+e[a].s*all(b,d)+e[b].s*all(a,c) ans[i]=ans[a]+ans[b]+e[a].se[b].se[i].l+e[a].sall(b,d)+e[b].sall(a,c).

  • a l l ( T , x ) all(T,x) all(T,x)

  • 1、如果 e [ T ] . s = 1 e[T].s=1 e[T].s=1,返回值为 0 0 0

  • 2、如果 x x x在左子树上,返回值为 a l l ( a , x ) + a l l ( b , d ) + e [ b ] . s ∗ ( e [ T ] . l + t o ( a , x , c ) ) all(a,x)+all(b,d)+e[b].s*(e[T].l+to(a,x,c)) all(a,x)+all(b,d)+e[b].s(e[T].l+to(a,x,c))

  • 3、如果 x x x在右子树上,返回值为 a l l ( b , x − e [ a ] . s ) + a l l ( a , c ) + e [ a ] . s ∗ ( e [ T ] . l + t o ( b , x − e [ a ] . s , d ) ) all(b,x-e[a].s)+all(a,c)+e[a].s*(e[T].l+to(b,x-e[a].s,d)) all(b,xe[a].s)+all(a,c)+e[a].s(e[T].l+to(b,xe[a].s,d))。( x − e [ a ] . s x-e[a].s xe[a].s是转化为右子树中的编号)

  • t o ( T , x , y ) to(T,x,y) to(T,x,y):保证 x ≤ y x≤y xy

  • 1、如果 x = y x=y x=y,返回值为 0 0 0

  • 2、如果 x , y x,y x,y都在左子树上,返回值为 t o ( a , x , y ) to(a,x,y) to(a,x,y)

  • 3、如果 x , y x,y x,y都在右子树上,返回值为 t o ( b , x , y ) to(b,x,y) to(b,x,y)

  • 4、如果 x x x在左子树上, y y y在右子树上,返回值为 t o ( a , x , c ) + e [ T ] . l + t o ( b , y − e [ a ] . s , d ) to(a,x,c)+e[T].l+to(b,y-e[a].s,d) to(a,x,c)+e[T].l+to(b,ye[a].s,d)。( y − e [ a ] . s y-e[a].s ye[a].s是转化为右子树中的编号)

  • 这样就能解决 60 60 60分了。

  • 因为有很多重复计算的,所以考虑记忆化,

  • 但是函数带的参数过大,需要用哈希表来存储。

  • 对于 a l l all all函数,存入 t ∗ x t*x tx

  • 对于 t o to to函数,存入 t ∗ x ∗ y t*x*y txy

  • 模数取 2 ∗ 1 0 5 + 9 2*10^5+9 2105+9,哈希表大小 5 ∗ 1 0 5 5*10^5 5105即可。

  • 记得每次计算都要模数,否则会出现负数,而且错误总是难以查出。

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long 
#define md 1000000007
#define z 200009
LL m,tn;
struct node
{
	LL s,l,a,b,c,d;
}e[61];
LL ans[61];
struct
{
	LL u,v,s,t,id;
}hx[500010],hs[500010];
LL hash(LL t,LL v)
{
	LL x=(t%z)*(v%z)%z;
	while(hs[x].id==tn+1)
	{
		if(hs[x].t==t&&hs[x].v==v) return x;
		x++;		
		if(x>500000) x=1;
	}
	return -x;
}
LL haxi(LL t,LL u,LL v)
{
	LL x=(t%z)*(u%z)%z*(v%z)%z;
	while(hx[x].id==tn+1)	
	{
		if(hx[x].t==t&&hx[x].u==u&&hx[x].v==v) return x;
		x++;
		if(x>500000) x=1;
	}
	return -x;
}
LL to(LL t,LL u,LL v)
{
	LL ss=e[e[t].a].s,sum;
	LL hs1=haxi(t,u,v);
	if(hs1>0) return hx[hs1].s; 
	if(u>v) 
	{
		LL tp=u;u=v,v=tp;
	}
	if(u==v) return 0;
	if(v<=ss) sum=to(e[t].a,u,v);
	else if(u>ss) sum=to(e[t].b,u-ss,v-ss);
	else sum=(to(e[t].a,u,e[t].c)+to(e[t].b,v-ss,e[t].d)+e[t].l)%md;
	hx[-hs1].t=t;
	hx[-hs1].s=sum%md;
	hx[-hs1].u=u;
	hx[-hs1].v=v;
	hx[-hs1].id=tn+1;
	return sum;
}
LL all(LL t,LL v)
{
	LL ss=e[e[t].a].s,sum;
	LL hs1=hash(t,v);
	if(hs1>0) return hs[hs1].s;
	if(e[t].s==1) return 0;
	if(v<=ss) sum=(all(e[t].a,v)+all(e[t].b,e[t].d)+e[e[t].b].s%md*e[t].l%md+e[e[t].b].s%md*to(e[t].a,v,e[t].c)%md)%md;
	else sum=(all(e[t].b,v-ss)+all(e[t].a,e[t].c)+e[e[t].a].s%md*e[t].l%md+e[e[t].a].s%md*to(e[t].b,v-ss,e[t].d)%md)%md;
	hs[-hs1].t=t;
	hs[-hs1].s=sum%md;
	hs[-hs1].v=v;
	hs[-hs1].id=tn+1;
	return sum;
}
int main()
{
	scanf("%lld",&tn);
	while(tn--)
	{
		scanf("%lld",&m);
		e[0].s=1;
		LL a,b,c,d;
		for(LL i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e[i].l);
			c++,d++;
			e[i].a=a,e[i].b=b,e[i].c=c,e[i].d=d;
			e[i].s=e[a].s+e[b].s;
			ans[i]=ans[a]+ans[b]+(e[a].s%md)*(e[b].s%md)%md*e[i].l%md+e[a].s%md*all(b,d)%md+e[b].s%md*all(a,c)%md;
			ans[i]%=md;
			printf("%lld\n",ans[i]);
		}
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值