联合权值(codevs 3728)题解
【问题描述】
无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi
, 每条边的长度均为 1。图上两点(u, v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对(u, v),若它们的距离为 2,则它们之间会产生Wu
×Wv
的联合权值。请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
【样例输入】
5
1 2
2 3
3 4
4 5
1 5 2 3 10
【样例输出】
20 74
【解题思路】
第一次去提高组碰到的题目……没错NOIP2014……当时我那叫一个聪明啊,直接开了个邻接矩阵。数据范围那叫一个小啊,n<=200000啊,然后华丽丽地全部MLE……好吧,其实当时是因为不会邻接表,而且当时我的思路有点问题……数组范围改小后也只过了3个点。
下面来说正解。
首先这道题必须要用邻接表来存储!(不会邻接表的自行百度,把程序抄上去自己debug,自学是丝毫没有问题的)然后,由于邻接表的特性,我们可以直接求出所有两点直接距离为2的点对,即一个点直接连接的所有点的距离均为2,对于每一个点所直接连接的点,我们可以存储第一大和第二大的点,这两个点的乘积最大的便是第一个问题的解。
然后对于每一个点,如果我们一个个点对去找,是会TLE3个点的,那么最好的方法是这样的:我们知道一个点所直接连接的点两两之间都是有序点对,那么,当我们搜到第n个点时,它所组成的所有点对的联合权值为前面所有点的权值加起来与它相乘(这是关键部分!!!不懂的自己算一算,想一想),由于题目中(1,3)和(3,1)算两个不同的有序点对,因此我们需要把答案乘2。注意:在算答案的时候要边算边取模。下面贴代码。
【代码实现】
1 type rec=record 2 c,next:longint; 3 end; 4 var g:array[0..200010] of rec; 5 e:array[0..400010] of rec; 6 efree,n,u,v,i,j,k,max,ans,max1,max2,sum:longint; 7 w:array[0..200010] of longint; 8 procedure add(x:longint;var p:rec); 9 begin 10 e[efree].c:=x; 11 e[efree].next:=p.next; 12 p.next:=efree; 13 inc(efree); 14 end; 15 begin 16 readln(n); 17 for i:=1 to n do 18 g[i].next:=-1; 19 efree:=1; 20 for i:=1 to n-1 do 21 begin 22 readln(u,v); 23 add(u,g[v]); 24 add(v,g[u]); 25 end; 26 for i:=1 to n do 27 read(w[i]); 28 for i:=1 to n do 29 begin 30 j:=g[i].next; 31 sum:=0; 32 max1:=0; 33 max2:=0; 34 while j<>-1 do 35 begin 36 k:=w[e[j].c]; 37 if k>max1 then 38 max1:=k 39 else 40 if k>max2 then 41 max2:=k; 42 ans:=ans+(sum mod 10007)*k*2 mod 10007; 43 ans:=ans mod 10007; 44 inc(sum,k); 45 j:=e[j].next; 46 end; 47 if max1*max2>max then 48 max:=max1*max2; 49 end; 50 writeln(max,' ',ans); 51 end.