NOIP2014day1之联合权值

NOIP2014day1有个题目叫联合权值

题目描述

    无向连通图 G 有 n 个点,n-1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi, 每条边的长度均为 1。图上两点(u, v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对(u, v),若它们的距离为 2,则它们之间会产生Wu×Wv的联合权值。

请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入格式

  第一行包含 1 个整数 n。

  接下来 n-1 行,每行包含 2 个用空格隔开的正整数 u、v,表示编号为 u 和编号为 v 的点 之间有边相连。

  最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示 图 G 上编号为 i 的点的权值为Wi

输出格式

  输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值 和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。

样例输入

1 2 

2 3 

3 4 

4 5 

1 5 2 3 10 

样例输出

20 70

限制

对于 30%的数据,1 < n ≤ 100;

对于 60%的数据,1 < n ≤ 2000;

对于 100%的数据,1 < n ≤ 200,000,0 < Wi ≤ 10,000。

 

这么一道题,有一个很显而易见的做法,N^2枚举,当然,只能拿60分

题目上说n-1条边的无向连通图,实际上就是告诉我们这是一棵树,那么很显然是不存在环的

而距离为2的点,实际上就是与每个点直接存在边相连的任意两个不同点

那么很轻易可以想到要枚举中间点,然后对于它周围一圈的点进行计算

因为是树不存在环,所以可以确保计算不重复

最大值很轻易,就是周围一圈点中最大两个点的乘积取最大值

那么剩下就是求和的问题了

如果不考虑时间和空间,就是双重循环跑一趟就好了

然后可以发现,对于周围这一圈每个点都要与其他点相乘

那么对于每个点,设sum=周围一圈点的权值和,Wi

又因为是有序点对,(i,j)和(j,i)被认为不同

那么每个点对答案都有一个Wi*(sum-Wi)的贡献,那么只要跑一趟就可以了

复杂度是O(N)的,因为每条边最多两次被参与计算

代码在下面

 const tt=10007;
 var link,a:array[0..200005]of longint;
     son,next:array[0..400005]of longint;
     n,tot,ans1,ans:longint;
 procedure add(x,y:longint);
  begin
   inc(tot);son[tot]:=y;next[tot]:=link[x];link[x]:=tot;
  end;
 procedure init;
  var x,y,i,j:longint;
   begin
    assign(input,'link.in');reset(input);
    assign(output,'link.out');rewrite(output);
    fillchar(link,sizeof(link),0);
    readln(n);
    tot:=0;
    for i:=1 to n-1 do
     begin
      readln(x,y);
      add(x,y);add(y,x);
     end;
    for i:=1 to n do read(a[i]);
   end;
 procedure main;
  var i,j,x,y,sum:longint;
   begin
    a[0]:=0;
    ans:=0;ans1:=0;
    for i:=1 to n do
     begin
      sum:=0;x:=0;
      j:=link[i];
      while j<>0 do
       begin
        if a[son[j]]>a[x] then x:=son[j];
        sum:=(sum+a[son[j]])mod tt;
        j:=next[j];
       end;
      j:=link[i];y:=0;
      while j<>0 do
       begin
        if (a[son[j]]>a[y])and(son[j]<>x) then y:=son[j];
        ans:=(ans+a[son[j]]*(sum+tt-a[son[j]]))mod tt;
        j:=next[j];
       end;
       if a[x]*a[y]>ans1 then ans1:=a[x]*a[y];
     end;
    end;
 procedure print;
  begin
   writeln(ans1,' ',ans);
   close(input);close(output);
  end;
 begin
  init;
  main;
  print;
 end.

还发生了小插曲

我写好后,测评只有20

和学长认认真真检查了一个晚上,发现取模错了,应该对10007,我写成了70007

然后好不服,因为百度文库点开的题目就是70007

最后,学长语重心长的说“听我的,撞墙吧,网上几千份,你刚好点开了错了”

QAQ尴尬

【写的有漏洞的,欢迎路过大神吐槽】

2016-11-01 13:37:00

Ending.

posted @ 2016-11-01 13:37  白云千载空悠悠  阅读(199)  评论(0编辑  收藏  举报