[NOIP 2014] 联合权值

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

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

首先我们注意第一句话,说的那么复杂,其实就告诉我们,这是一棵无根树,再看问题,统计所有距离为2的点对的权值乘积之和及最大值,要就是一张图只能爆搜,但是树,我们可以直接枚举点,显然距离为2的点对之间有且只有一个节点,不存在两个点之间有多条路径,这是树的性质决定的,也就是说显然不会出现重复统计。因此我们只需要枚举每个点,从这个点出发到达的所有点两两之间都可以组成答案。

直接暴力枚举显然不行,(n^2)复杂度显然过不去,但是我们可以采用一些优化,是一种类似于前缀和的思想。

假设现在我们枚举点u,而点u有若干个儿子,我们用邻接表存图可以得到一个儿子的序列,假设我们现在访问到儿子v,那么儿子v对答案的贡献就是前面访问的所有儿子的权值与当前点v权值乘积之和,前面所有儿子的权值之和显然可以前缀和优化一下,这样无序点对的所有答案就可以求出来了,而题中要求出有序的,因此答案*2即可。

再考虑最大值,这里需要一些贪心的思路,我们统计时维护前缀最大值,这个最大值和当前枚举的儿子v之间就有可能产生最大值,然后更新前缀最大值和答案就行了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<set>
#include<queue>
#include<vector>
#include<string>
#pragma GCC optimaze(2)
#pragma GCC optimaze(3)
using namespace std;

#define P system("pause");
#define B printf("Break
");
#define A(x) cout << #x << " " << (x) << endl;
#define AA(x,y) cout << #x << " " << (x) << " " << #y << " " << (y) << endl;
#define ll long long
#define inf 1000000000
#define linf 10000000000000000

int read()
{
    int x = 0,f = 1;
    char c = getchar();
    while(c < '0' || c > '9')
    {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        x = (x << 3) + (x << 1) + c - '0';
        c = getchar();
    }
    return f * x;
}
#define N 200010
#define mod 10007
ll val[N],n;
vector<int>g[N];
int main()
{
    n = read();
    for(int i = 1;i < n;i++)
    {
        int u = read(),v = read();
        g[u].push_back(v);
        g[v].push_back(u);
    }
    ll Max = 0,ans = 0;
    for(int i = 1;i <= n;i++) val[i] = read();
    for(int u = 1;u <= n;u++)
    {
        ll sum = 0,tp = 0;
        for(int j = 0;j < g[u].size();j++)
        {
            int v = g[u][j];
            Max = max(Max,tp * val[v]);
            tp = max(tp,val[v]);
            ans += val[v] * sum % mod;
            sum += val[v];
        }
    }
    cout << Max << " " << ans * 2 % mod << endl;
}
原文地址:https://www.cnblogs.com/lijilai-oi/p/11606303.html