P1351 联合权值

题目描述

无向连通图 GG 有 nn 个点, n-1n1 条边。点从 11 到 nn 依次编号,编号为 ii 的点的权值为 W_iWi ,每条边的长度均为 11 。图上两点 (u, v)(u,v) 的距离定义为 uu 点到 vv 点的最短距离。对于图 GG 上的点对 (u, v)(u,v) ,若它们的距离为 22 ,则它们之间会产生 W_v \times W_uWv×Wu 的联合权值。

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

输入输出格式

输入格式:

第一行包含 11 个整数 nn 。

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

最后 11 行,包含 nn 个正整数,每两个正整数之间用一个空格隔开,其中第 ii 个整数表示图 GG 上编号为 ii 的点的权值为 W_iWi

输出格式:

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

输入输出样例

输入样例#1:  复制
5  
1 2  
2 3
3 4  
4 5  
1 5 2 3 10 
输出样例#1:  复制
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 < n \leq 1001<n100 ;

对于60%的数据, 1 < n \leq 20001<n2000 ;

对于100%的数据, 1 < n \leq 200000, 0 < W_i \leq 100001<n200000,0<Wi10000 。

保证一定存在可产生联合权值的有序点对。

 这题主要是在时间上比较有要求,看到这题一开始想到的都是先便利每个距离为二的点来求可这样时间复杂度就不小了,我也是这样T了几发。

而这题因该是用中间点来做标准,每个点所连接到的每两个点,这个点就是它的中间点所以这样就只需要遍历一次就行了,先用MAX1代表这个点所连接的最大点,用MAX2表示第二大的,这两个相乘就是最大的边了,遍历所有就可以了。

而算总长的话,就要算出它遍历的每两个点相乘累加就行了,最后记得乘2,因为是双向都要加,还不懂就看看代码。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int NN = 200005;
long long head[NN], size, d[NN], n, m, mm, sum;

struct Edge
{
    long long v, next;
}E[NN*4];

void init()
{
    mm = 0;
    memset(head,-1,sizeof(head));
    size = 0;
    sum = 0;
}

void add(long long u, long long v)
{
    E[size].v = v,E[size].next = head[u];
    head[u] = size++;
}

void bfs(long long u)
{
    long long max1, max2, sum1, sum2;
    max1 = max2 = sum1 = sum2 = 0;
    for (int i = head[u];~i;i = E[i].next)
    {
        if (max1 < d[E[i].v])
            max1 = d[E[i].v];
        else if (max2 < d[E[i].v])
            max2 = d[E[i].v];
        sum = (sum + d[E[i].v] * sum2)%10007 ;//ai*(ai+ai-1+..) = 每个边相乘在相加
        sum2 = (sum2 + d[E[i].v])%10007;
    }
    mm = max(max1*max2,mm);//这里就是求单独最大的
}

int main()
{
    long long i, j, u, v;
    while (~scanf("%lld",&n))
    {
        init();
        for (i = 1;i < n;i++)
        {
            scanf("%lld%lld",&u,&v);
            add(u,v);
            add(v,u);
        }
        for (i = 1;i <= n;i++)
            scanf("%lld",&d[i]);
        for (i = 1;i <= n;i++)
            bfs(i);
        printf("%lld %lld\n",mm,(sum*2)%10007);//最后也不能忘除余
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值