• [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;
    }
    
  • 相关阅读:
    【KMP】Period
    【KMP】Cyclic Nacklace
    【KMP】Oulipo
    【KMP】Number Sequence
    二分
    Stock 贪心经典 Zoj2921
    D. Block Tower
    烦人的dp
    搜索
    hdu 一个人的旅行
  • 原文地址:https://www.cnblogs.com/lijilai-oi/p/11606303.html
Copyright © 2020-2023  润新知