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

题目地址

描述

n + 1 n+1 n+1棵树 T 0 , T 1 , … , T n T_0,T_1,\dots,T_n T0,T1,,Tn T 0 T_0 T0只有 0 0 0号节点,对于 T i ( i ∈ [ 1.. m ] ) T_i(i\in [1..m]) Ti(i[1..m]),给出 a i , 1 , a i , 2 , b i , 1 , b i , 2 , l e n i a_{i,1},a_{i,2},b_{i,1},b_{i,2},len_i ai,1,ai,2,bi,1,bi,2,leni,表示这棵树是由 T a i , 1 T_{a_{i,1}} Tai,1 T b i , 1 T_{b_{i,1}} Tbi,1构成,且 T a i , 1 T_{a_{i,1}} Tai,1的第 a i , 2 a_{i,2} ai,2个点与 T b i , 1 T_{b_{i,1}} Tbi,1的第 b i , 2 b_{i,2} bi,2个点连接一条长度为 l e n i len_i leni的边。在 T i T_i Ti中,保持 T a i , 1 T_{a_{i,1}} Tai,1中的所有节点编号不变,如果 T a i , 1 T_{a_{i,1}} Tai,1中有 s s s个节点,他会把 T b i , 1 T_{b_{i,1}} Tbi,1中的所有节点的编号加上 s s s

对于 T i ( i ∈ [ 1.. n ] ) T_i(i\in [1..n]) Ti(i[1..n]),求位于 T i T_i Ti中互不相同的两个点的距离之和。

n ≤ 1 0 2 n\leq 10^2 n102 0 ≤ a i , b i < i 0 \leq ai, bi < i 0ai,bi<i 0 ≤ l i ≤ 1 0 9 0\leq li\leq 10^9 0li109

分析

A n s i Ans_i Ansi表示 T i T_i Ti的答案, s i z e i size_i sizei表示 ∣ T i ∣ \left | T_i \right | Ti t o p v , x top_{v,x} topv,x表示 T v T_v Tv中所有节点与 x x x的距离之和。则

A n s i = A n s a i , 1 + A n s b i , 1 + t o p a i , 1 , a i , 2 × s i z e b i , 1 + t o p b i , 1 , b i , 2 × s i z e a i , 1 + l e n i × s i z e a i , 1 × s i z e b i , 1 Ans_i=Ans_{a_{i,1}}+Ans_{b_{i,1}}+top_{a_{i,1},a_{i,2}}\times size_{b_{i,1}}+top_{b_{i,1},b_{i,2}}\times size_{a_{i,1}}+len_i\times size_{a_{i,1}}\times size_{b_{i,1}} Ansi=Ansai,1+Ansbi,1+topai,1,ai,2×sizebi,1+topbi,1,bi,2×sizeai,1+leni×sizeai,1×sizebi,1

定义solve(v,x)求解 t o p v , x top_{v,x} topv,x,设 D i s v , x , y Dis_{v,x,y} Disv,x,y表示在 T v T_v Tv x → y x\rightarrow y xy的距离。

考虑将 T v T_v Tv分成 T a v , 1 , T b v , 1 T_{a_{v,1}},T_{b_{v,1}} Tav,1,Tbv,1

  • v = 0 v=0 v=0solve(v,x)返回 0 0 0
  • x < s i z e a v , 1 x<size_{a_{v,1}} x<sizeav,1。说明 x x x T a v , 1 T_{a_{v,1}} Tav,1中,solve(v,x)返回 t o p a v , 1 , x + t o p b v , 1 , b v , 2 + ( l e n v + D i s a v , 1 , a v , 2 , x ) × s i z e b v , 1 top_{a_{v,1},x}+top_{b_{v,1},b_{v,2}}+(len_v+Dis_{a_{v,1},a_{v,2},x})\times size_{b_{v,1}} topav,1,x+topbv,1,bv,2+(lenv+Disav,1,av,2,x)×sizebv,1
  • x ≥ s i z e a v , 1 x\geq size_{a_{v,1}} xsizeav,1。同理得到一个类似的返回值。

于是调用solve(v,x)即可得到 t o p v , x top_{v,x} topv,x。问题转化为求解 D i s v , x , y Dis_{v,x,y} Disv,x,y

  • x = y x=y x=y v = 0 v=0 v=0,返回 0 0 0
  • x , y x,y x,y T v T_v Tv中同一棵树中,则继续递归下去。
  • 否则,令 x < y x<y x<y,则 D i s v , x , y ← D i s a v , 1 , a v . 2 , x + D i s b v , 1 , b v , 2 , y + l e n v Dis_{v,x,y}\leftarrow Dis_{a_{v,1},a_{v.2},x}+Dis_{b_{v,1},b_{v,2},y}+len_v Disv,x,yDisav,1,av.2,x+Disbv,1,bv,2,y+lenv

H a s h Hash Hash记忆化一下即可。

时间复杂度 O ( n 3 ) O(n^3) O(n3)

代码

const
        mo=1000000007; mo1=449363; mo2=447001;
var
        t,n,i:longint;
        a,b:array[0..60,0..2] of int64;
        hash:array[0..500000,1..3] of int64;
        hash2:array[0..500000,1..4] of int64;
        ans,len,size,size1:array[0..60] of int64;
procedure swap(var x,y:int64);
var
        z:int64;
begin
        z:=x; x:=y; y:=z;
end;

procedure put(x:longint;y,z:int64);
var
        k:longint;
begin
        k:=(sqr(x)+sqr(y mod mo1)) mod mo1;

        while hash[k,3]<>0 do inc(k);

        hash[k,1]:=x; hash[k,2]:=y; hash[k,3]:=z;
end;

function find(x:longint;y:int64):longint;
var
        k:longint;
begin
        k:=(sqr(x)+sqr(y mod mo1)) mod mo1;

        while hash[k,3]<>0 do
        begin
                if (hash[k,1]=x) and (hash[k,2]=y) then exit(hash[k,3]);

                inc(k);
        end;

        exit(0);
end;

procedure put2(x:longint;y,z,o:int64);
var
        k:longint;
begin
        k:=(sqr(x)+sqr(y mod mo2)+sqr(z mod mo2)) mod mo2;

        while hash2[k,4]<>0 do inc(k);

        hash2[k,1]:=x; hash2[k,2]:=y; hash2[k,3]:=z; hash2[k,4]:=o;
end;

function find2(x:longint;y,z:int64):longint;
var
        k:longint;
begin
        k:=(sqr(x)+sqr(y mod mo2)+sqr(z mod mo2)) mod mo2;

        while hash2[k,3]<>0 do
        begin
                if (hash2[k,1]=x) and (hash2[k,2]=y) and (hash2[k,3]=z) then exit(hash2[k,4]);

                inc(k);
        end;

        exit(0);
end;

function dis(v:longint;x,y:int64):int64;
var
        now:int64;
begin
        if (v=0) or (x=y) then exit(0);

        now:=find2(v,x,y); if now>0 then exit(now);

        if (x<size[a[v,1]]) and (y<size[a[v,1]]) then
                now:=dis(a[v,1],x,y)
        else
        if (x>=size[a[v,1]]) and (y>=size[a[v,1]]) then
                now:=dis(b[v,1],x-size[a[v,1]],y-size[a[v,1]])
        else
        begin
                if x>y then swap(x,y);

                now:=(dis(a[v,1],a[v,2],x)+dis(b[v,1],b[v,2],y-size[a[v,1]])+len[v]) mod mo;
        end;

        put2(v,x,y,now); exit(now);
end;

function solve(v:longint;x:int64):int64;
var
        now:int64;
begin
        if v=0 then exit(0);

        now:=find(v,x); if now>0 then exit(now);

        if x<size[a[v,1]] then
                now:=solve(a[v,1],x)+solve(b[v,1],b[v,2])+(len[v]+dis(a[v,1],a[v,2],x)) mod mo*size1[b[v,1]] mod mo
        else
                now:=solve(b[v,1],x-size[a[v,1]])+solve(a[v,1],a[v,2])+(len[v]+dis(b[v,1],b[v,2],x-size[a[v,1]])) mod mo*size1[a[v,1]] mod mo;

        now:=now mod mo; put(v,x,now); exit(now);
end;

begin
        readln(t);

        while t>0 do
        begin
                readln(n); dec(t);

                size[0]:=1; size1[0]:=1; fillchar(hash,sizeof(hash),0); fillchar(hash2,sizeof(hash2),0);

                for i:=1 to n do
                begin
                        readln(a[i,1],b[i,1],a[i,2],b[i,2],len[i]); len[i]:=len[i] mod mo; size[i]:=size[a[i,1]]+size[b[i,1]]; size1[i]:=size[i] mod mo;
                end;

                ans[0]:=0;

                for i:=1 to n do
                begin
                        ans[i]:=ans[a[i,1]]+ans[b[i,1]];
                        ans[i]:=ans[i]+solve(a[i,1],a[i,2])*size1[b[i,1]] mod mo;
                        ans[i]:=ans[i]+solve(b[i,1],b[i,2])*size1[a[i,1]] mod mo;
                        ans[i]:=ans[i]+len[i]*size1[a[i,1]] mod mo*size1[b[i,1]] mod mo;
                        ans[i]:=ans[i] mod mo;

                        writeln(ans[i]);
                end;
        end;
end.


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值