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 0≤ai,bi<i, 0 ≤ l i ≤ 1 0 9 0≤li≤10^9 0≤li≤109, 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
m≤8
对于60% 的数据,
m
≤
16
m≤16
m≤16
对于100% 的数据,
1
≤
m
≤
60
1≤m≤60
1≤m≤60,
T
≤
100
T≤100
T≤100
题解
-
点数到最后可能会有 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].a,e[i].b,e[i].c,e[i].d,e[i].l如题目中的 a , b , c , d , l a,b,c,d,l a,b,c,d,l,记录 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].a,b=e[i].b,c=e[i].c,d=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].s∗e[b].s∗e[i].l+e[a].s∗all(b,d)+e[b].s∗all(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,x−e[a].s)+all(a,c)+e[a].s∗(e[T].l+to(b,x−e[a].s,d))。( x − e [ a ] . s x-e[a].s x−e[a].s是转化为右子树中的编号)
-
t o ( T , x , y ) to(T,x,y) to(T,x,y):保证 x ≤ y x≤y x≤y,
-
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,y−e[a].s,d)。( y − e [ a ] . s y-e[a].s y−e[a].s是转化为右子树中的编号)
-
这样就能解决 60 60 60分了。
-
因为有很多重复计算的,所以考虑记忆化,
-
但是函数带的参数过大,需要用哈希表来存储。
-
对于 a l l all all函数,存入 t ∗ x t*x t∗x,
-
对于 t o to to函数,存入 t ∗ x ∗ y t*x*y t∗x∗y,
-
模数取 2 ∗ 1 0 5 + 9 2*10^5+9 2∗105+9,哈希表大小 5 ∗ 1 0 5 5*10^5 5∗105即可。
-
记得每次计算都要模数,否则会出现负数,而且错误总是难以查出。
代码
#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
",ans[i]);
}
}
return 0;
}