[NOIP2014]联合权值 D1 T2

题目描述

Description

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

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

Input

第一行包含 1个整数 n。
接下来 n-1行,每行包含 2个用空格隔开的正整数 u、v,表示编号为 u和编号为 v的点之间有边相连。
最后 1行,包含 n个正整数,每两个正整数之间用一个空格隔开,其中第 i个整数表示图 G上编号为 i的点的权值为 Wi

Output

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

Sample Input

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

Sample Output

20 74

HINT

【样例说明】

本例输入的图如上所示,距离为 2的有序点对有(1,3)、(2,4)、(3,1)、(3,5)、(4,2)、(5,3)。其联合权值分别为 2、15、2、20、15、20。其中最大的是 20,总和为 74。

【数据说明】
对于 30%的数据,1< n ≤100;
对于 60%的数据,1< n ≤2000;
对于 100%的数据,1< n ≤200,000,0< Wi ≤10,000。

题目分析:

提高组竟然考了一道如此裸的树形DP
可能产生权值的情况(距离为2):一个点的儿子和父亲 一个点的两个儿子
这样 我们对于每个点 求出这些东西 加和*2就好了
但是对于情况二 我们想到暴力枚举儿子加和 但是O(n^2)的复杂度过不了 怎么办?
这里需要一下优化:
ans={son1*son2+son1*son3+...+son*son(n)}+{son2*son3+son2*son4+...+son2*son(n)}+...+{son(n-1)*son(n)}
= son1*(∑(son1~son(n)-son1)+son2*(∑(son1~sonn)-son1-son2)+...+son(n-1)*(∑(son1~son(n))-∑(son1~son(n-1)))
O(n) 解决

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
long long ans;
long long ans2;
long long w[200100];
int head[200100],net[400100],to[400100];
int tot;
void add(int x,int y)
{
    net[++tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
void check(long long &x,long long &y,long long c)
{
    if(x<=c) y=x,x=c;
    else if(y<=c) y=c;
    return; 
}
void dfs(int x,int temp)
{
    long long tmp1=-1,tmp2=-1,sum=0;
    for(int i=head[x];i;i=net[i])
    {
        if(temp!=to[i])
        {
            dfs(to[i],x);
            check(tmp1,tmp2,w[to[i]]);
        }
    }
    if(tmp1==-1&&tmp2==-1) return ;
    if(temp!=-1) ans=max(ans,tmp1*w[temp]);
    if(tmp2!=-1) ans=max(ans,tmp1*tmp2);
    sum=0;
    for(int i=head[x];i;i=net[i])
        if(to[i]!=temp) sum=(sum+w[to[i]]);
    for(int i=head[x];i;i=net[i])
    {
        if(to[i]==temp) continue;
        sum-=w[to[i]];
        ans2=(ans2+sum%10007*w[to[i]])%10007;
        if(temp!=-1) ans2=(ans2+w[temp]*w[to[i]])%10007;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    for(int i=1;i<=n;i++)
        scanf("%lld",&w[i]);
    dfs(1,-1);
    printf("%lld %lld",ans,(ans2%10007*2)%10007);
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注