zoukankan      html  css  js  c++  java
  • NOIP 2014 联合权值

    题目描述

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

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

    输入输出格式

    输入格式:

    输入文件名为link .in。

    第一行包含1 个整数n 。

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

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

    输出格式:

    输出文件名为link .out 。

    输出共1 行,包含2 个整数,之间用一个空格隔开,依次为图G 上联合权值的最大值

    和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007 取余。

    输入输出样例

    输入样例#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≤ 100 ;

    对于60% 的数据,1 < n≤ 2000;

    对于100%的数据,1 < n≤ 200 , 000 ,0 < wi≤ 10, 000 。

    这个题以乱搞为主。

    我们可以发现直接枚举一个点两边的点就相当于同时枚举两点了,所以我们直接枚举一个点两边的点。考虑这样复杂度还是太高(n^2),我们尝试优化。

    首先,对于一个点的最大的联合权值,一定是其所有出点权值中最大和次大的乘积(如果只有一个,则次大不存在,联合权值为0),我们可以在枚举点的时候顺便处理出最大和次大值来。其次,对于一个点的联合权值之和,为Σw[i]*w[j],(i,j为点u的出点,且i,j有序),我们从这个式子中不难发现,一个出点i可以和点u的任意一个出点j(i≠j)产生一定的联合权值,总的来说就是i对答案的贡献为w[i]*(w[j1]+w[j2]+...+w[jd])(在这个式子中不含w[i]),(d为u的出度),如果令sum表示u的所有出点的权值之和,那么i对答案的贡献也可以写成w[i]*(sum-w[i]),那么,我们可以预处理出sum来,然后对出点进行一次遍历,计算出所有的w[i]*(sum-w[i]),则Σw[i]*(sum-w[i]),就是点u对答案的贡献。最后相加即可。

    这题的难点就在于开脑洞,和各种数学知识乱搞就行了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N=2e5+5;
    inline int read()
    {
        int ret=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9')
        {if(c=='-') f=-1;c=getchar();}
        while(c>='0'&&c<='9')
        {ret=ret*10+c-'0';c=getchar();}
        return ret*f;
    }
    int n;
    struct edge{
        int from,to;
    }e[N<<1];
    int head[N],nxt[N<<1],tot=0;
    void adde(int f,int t)
    {
        e[++tot]=(edge){f,t};
        nxt[tot]=head[f];
        head[f]=tot;
    }
    int w[N];
    int maxw=0,sumw=0;
    const int mod=10007;
    void solve()
    {
        for(int i=1;i<=n;i++)
        {
            int fir=0,sec=0;
            int sum=0;
            for(int j=head[i];j;j=nxt[j])
            {
                int v=e[j].to;
                if(w[v]>=fir)
                {
                    if(w[v]==fir) sec=fir;
                    fir=w[v];
                }
                else
                {
                    sec=max(sec,w[v]);
                }
                sum+=w[v];
                sum%=mod;
            }
            maxw=max(maxw,fir*sec);
            for(int j=head[i];j;j=nxt[j])
            {
                int v=e[j].to;
                sumw=(sumw%mod+(w[v]%mod)*(((sum%mod-w[v]%mod)+mod)%mod))%mod;
                sumw%=mod;
            }
        }
    }
    int main()
    {
        n=read();
        int a,b;
        for(int i=1;i<=n-1;i++)
        {
            a=read(),b=read();
            adde(a,b);
            adde(b,a);
        }
        for(int i=1;i<=n;i++) w[i]=read();
        solve();
        printf("%d %d",maxw,sumw%mod);
        return 0;
    }
  • 相关阅读:
    Markdown示例
    初识Markdown
    Ubuntu源整理
    Vi编辑器入门
    Ubuntu,Linux入门系统
    [其他]volatile 关键字
    Qt5:随窗口大小变化背景图片自动缩放的实现
    Qt5:不规则按钮的实现---通过贴图实现
    windows编程:创建DLL
    [转]Firefox拦截12306订票网站的解决办法
  • 原文地址:https://www.cnblogs.com/loi-frank/p/7788239.html
Copyright © 2011-2022 走看看