Noip2014 提高组 T2 联合权值 连通图+技巧

                                                                联合权值

描述

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

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

格式

输入格式

第一行包含 1 个整数 n。

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

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

输出格式

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

样例1

样例输入1[复制]

 
5
1 2
2 3
3 4
4 5
1 5 2 3 10

样例输出1[复制]

 
20 74

限制

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

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

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

 

解题报告:

  刚开始的时候,是直接想的暴力,用一个O(n^3)然后我想我一定是疯了,200000,数组也大的不行,就把电脑弄死机了。。。正在考试,学校的电脑一关机就什么都没有了,于是,苦逼的我只好重新把第一题写一遍,还好简单。可接下来,我就陷入了想第二题的深渊,无法自拔,看出他是一棵树后,开始学着树规编,可是树规也没学好,还不是特别熟悉,最后还剩半个小时,算了算了,写暴力。。啊,暴力还没有调出来,就交卷了。

  所以,要加快做题速度。

  正解:首先枚举每一个点,找他周围的最大与第二大的点,相乘的联合权值则为以这个点为中心的最大值,最后把所有点的最大权值扫一遍,然后就得到ans2.至于权值之和,我们举一个例子:

     i 这个点连了 1 2 3 三个点 ,那么权值之和即:w1*w2+w1*w3+w2*w3,技巧来了,根据乘法分配律,原式=w1*0+w2*w1+w3*(w1+w2) 

  根据这个,可以设一个数组存括号内的累和,然后与当前相乘,加入ans1;

代码如下:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=200005,inf=10007;
 5 int n,ans,maxx;
 6 int m1[maxn],m2[maxn],a[maxn],b[maxn],w[maxn];
 7 long long s[maxn],s2[maxn],sum;
 8 void doit(int x,int y)
 9 {
10     if (m1[x]==0) m1[x]=w[y];
11     else if (w[y]>m2[x])
12     {    
13         if (w[y]>m1[x]){
14             m2[x]=m1[x];
15             m1[x]=w[y];
16         }else{
17             m2[x]=w[y];
18         }
19     } 
20     s[x]=(s[x]+w[y]*s2[x])%inf;        
21     s2[x]+=w[y];
22 }
23 int main()
24 {
25     freopen("link.in","r",stdin);
26     freopen("link.out","w",stdout);
27     cin>>n;
28     for (int i=1;i<=n-1;i++)
29         scanf("%d%d",&a[i],&b[i]);
30     for (int i=1;i<=n;i++)
31            scanf("%d",&w[i]);
32     for (int i=1;i<=n;i++)
33     {
34         int x=a[i],y=b[i];
35         doit (x,y);
36         doit (y,x);
37     }
38     for (int i=1;i<=n;i++)
39     {
40         if (m1[i]*m2[i]>maxx) maxx=m1[i]*m2[i];
41         ans=(ans+s[i])%inf;
42     }
43     cout<<maxx<<" "<<(ans*2)%inf;
44     return 0;
45 }

值得注意的是:ans最后要乘2,因为有两个方向,另外,取mod 也很重要,特别是最后输出的时候。

我认为在这么多方法中,这个是最简单而易懂的。

转载于:https://www.cnblogs.com/lx0319/p/5683655.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值