zoukankan      html  css  js  c++  java
  • 『题解』洛谷P1351 联合权值

    更好的阅读体验

    Portal

    Portal1: Luogu

    Portal2: LibreOJ

    Description

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

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

    Input

    第一行包含(1)个整数(n)

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

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

    Output

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

    Sample Input

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

    Sample Output

    20 74
    

    Solution

    我们先看一下题目:无向连通图(mathrm G)(n)个点,(n - 1)条边。

    不难发现题目给出的是一颗树。

    我们看一个例子:

    这个图的联合权值和为(W_2 imes W_3 + W_4 imes W_5 + W_4 imes W_6 + W_5 imes W_6 + W_7 imes W_8)

    不难发现,我们求的是对于每一棵子树的非根节点的所有子结点两两相乘的权值和。但是我们对每一棵子树都遍历一遍显然要超时。我们可以找到如下性质:

    ((a + b) ^ 2 = a ^ 2 + b ^ 2 + 2ab​ \ (a + b + c) ^ 2 = a ^ 2 + b ^ 2 + c ^ 2 + 2ab+ 2ac + 2bc \ (a + b + c + d) = a ^ 2 + b ^ 2 + c ^ 2 + d ^ 2 + 2ab+ 2ac + 2ad + 2bc + 2bd + 2cd \ cdots cdots)

    我们要求的就是平方项后面的一半。就是 ( exttt{和的平方} - exttt{平方的和})

    统计最大值是只需要找出最大的两项,然后相乘就可以了。

    这样就这道题就解决了。

    Code

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    
    using namespace std;
    
    typedef long long LL;
    const int INF = 0x3f3f3f3f, MAXN = 400005, MAXM = 200005, mod = 10007;
    struct EDGE {
        int to, nxt;
    } edge[MAXN];
    int n, u, v, cnt, w[MAXM], head[MAXN];
    inline void addedge(int u, int v) {
        edge[++cnt].to = v; edge[cnt].nxt = head[u]; head[u] = cnt;
    }
    int main() {
        scanf("%d", &n);
        memset(head, -1, sizeof(head));
        for (int i = 1; i < n; i++) {
            scanf("%d%d", &u, &v);
            addedge(u, v); addedge(v, u);//加边
        }
        for (int i = 1; i <= n; i++)
            scanf("%d", &w[i]);
        LL Max = -INF, ans = 0;
        for (int i = 1; i <= n; i++) {
            LL Max1 = -INF, Max2 = -INF, tot1 = 0, tot2 = 0;//Max1表示最大的权值,Max2表示第二大的权值,tot1表示和的平方,tot2表示平方的和
            for (int j = head[i]; ~j; j = edge[j].nxt) {//遍历每一个点
                if (w[edge[j].to] > Max1) {
                    Max2 = Max1;
                    Max1 = w[edge[j].to];
                } else
                if (w[edge[j].to] > Max2 && w[edge[j].to] <= Max1) Max2 = w[edge[j].to];//找两个最大的
                tot1 += w[edge[j].to]; tot2 = (tot2 + w[edge[j].to] * w[edge[j].to]) % mod;//累计当前点的权值
            }
            tot1 = (tot1 % mod * tot1 % mod) % mod;//和的平方
            ans = (ans + tot1 - tot2 + mod) % mod;//累加答案
            Max = max(Max, Max1 * Max2);//找最大权值
        }
        printf("%lld %lld
    ", Max, ans);
        return 0;
    }
    

    Attachment

    测试数据下载:https://www.lanzous.com/i5q1vdg

  • 相关阅读:
    系统数据文件和信息之其他数据文件
    系统数据文件和信息之附加组ID
    系统数据文件和信息之组文件
    系统数据文件和信息之阴影口令
    系统数据文件和信息之口令文件
    PHP中的11个魔术方法总结:__construct,、__destruct、__call等
    php7新特性
    jquery操作select大全详解
    jquery实现select二级联动
    MySQL: ON DUPLICATE KEY UPDATE 用法
  • 原文地址:https://www.cnblogs.com/shenxiaohuang/p/11392927.html
Copyright © 2011-2022 走看看