联合权值
(link.cpp/c/pas)
【问题描述】
无向连通图G有n个点,n-1条边。点从1到n依次编号,编号为i的点的权值为Wi ,每条边的长度均为1。图上两点(u, v)的距离定义为u点到v点的最短距离。对于图G上的点对(u, v),若它们的距离为2,则它们之间会产生Wu×Wv的联合权值。
请问图G上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
【输入】
输入文件名为link.in。
第一行包含1个整数n。
接下来n-1行,每行包含2个用空格隔开的正整数u、v,表示编号为u和编号为v的点之间有边相连。
最后1行,包含n个正整数,每两个正整数之间用一个空格隔开,其中第i个整数表示图G上编号为i的点的权值为Wi。
【输出】
输出文件名为link.out。
输出共1行,包含2个整数,之间用一个空格隔开,依次为图G上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。
【输入输出样例】
link.in |
link.out |
5 1 2 2 3 3 4 4 5 1 5 2 3 10 |
20 74 |
【样例说明】
本例输入的图如上所示,距离为2的有序点对有(1,3)、(2,4)、(3,1)、(3,5)、(4,2)、(5,3)。其联合权值分别为2、15、2、20、15、20。其中最大的是20,总和为74。
【数据说明】
对于30%的数据,1<≤100;
对于60%的数据,1<≤2000;
对于100%的数据,1<≤200,000,0<Wi ≤10,000。
对于每个距离为2的店,都会有且只有一个唯一的中间点,所以我们只要枚举这个中间点,将与它相邻的点的权值两两相乘就可以了;然而因为两两相乘的时间复杂度是O(n^2)的,所以我们可以把两两相乘当做和平方减去平方和,总时间复杂度O(nlogn)。
然而CCF的数据好像有点奇怪超范围了...没事能AC就行...
代码:
1 const 2 modd=10007; 3 4 var 5 ot,ne,g,f:array[0..200001]of longint; 6 max,ans,n,e,a,b,i:longint; 7 8 procedure addedge(x,y:longint); 9 begin 10 ot[e]:=y; ne[e]:=g[x]; g[x]:=e; inc(e); 11 ot[e]:=x; ne[e]:=g[y]; g[y]:=e; inc(e); 12 end; 13 14 procedure work(x:longint); 15 var 16 a:array[0..200001]of longint; 17 a1,a2,i,p:longint; 18 begin 19 p:=g[x]; a[0]:=0; 20 a1:=0; a2:=0; 21 while p<>-1 do 22 begin inc(a[0]); a[a[0]]:=f[ot[p]]; p:=ne[p]; end; 23 if a[0]<2 then exit; 24 25 for i:=1 to a[0] do inc(a1,a[i]); 26 a1:=sqr((a1 mod modd))mod modd; 27 for i:=1 to a[0] do 28 begin 29 dec(a1,sqr(a[i])); 30 while a1<0 do inc(a1,modd); 31 end; 32 ans:=(ans+a1) mod modd; 33 34 a1:=0; 35 for i:=1 to a[0] do 36 if a[i]>a2 then 37 if a[i]<a1 then a2:=a[i] 38 else begin a2:=a1; a1:=a[i]; end; 39 if a1*a2>max then max:=a1*a2; 40 end; 41 42 procedure ready; 43 begin 44 readln(n); 45 fillchar(g,sizeof(g),255); e:=0; 46 for i:=1 to n-1 do 47 begin 48 readln(a,b); 49 addedge(a,b); 50 end; 51 for i:=1 to n do read(f[i]); 52 end; 53 54 procedure main; 55 begin 56 max:=0; ans:=0; 57 for i:=1 to n do work(i); 58 writeln(max,‘ ‘,ans); 59 end; 60 61 begin 62 ready; 63 main; 64 end.