zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    三元表达式 列表和字典推导式 函数对象 名称空间 作用域 global和nonlocal 函数装饰器 枚举对象
    函数参数 打散机制 字符串比较 返回值
    函数简介
    三种字符串的介绍 文件的读写
    字符编码
    数据类型及其常用方法 数据类型转换 可变与不可变 值拷贝与深浅拷贝
    流程控制 while和for循环
    变量命名规范 常量 输入和输出 注释 数据类型 运算符 逻辑运算符
    语言分类 编译型和解释型语言分析 环境变量 代码执行的方式 pip介绍 变量
    Python django tests
  • 原文地址:https://www.cnblogs.com/wry0112/p/6019144.html
Copyright © 2011-2022 走看看