• P1351 联合权值


    题目描述

    无向连通图 GG 有 nn 个点,n-1n1 条边。点从 11 到 nn 依次编号,编号为 ii 的点的权值为 W_iWi,每条边的长度均为 11。图上两点 (u, v)(u,v) 的距离定义为 uu 点到 vv 点的最短距离。对于图 GG 上的点对 (u, v)(u,v),若它们的距离为 22,则它们之间会产生W_v imes W_uWv×Wu 的联合权值。

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

    输入格式

    第一行包含 11 个整数 nn。

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

    最后 11 行,包含 nn 个正整数,每两个正整数之间用一个空格隔开,其中第 ii 个整数表示图 GG 上编号为 ii 的点的权值为 W_iWi

    输出格式

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

    输入输出样例

    输入 #1
    5  
    1 2  
    2 3
    3 4  
    4 5  
    1 5 2 3 10 
    输出 #1
    20 74

    说明/提示

    本例输入的图如上所示,距离为2 的有序点对有( 1,3)(1,3) 、( 2,4)(2,4) 、( 3,1)(3,1) 、( 3,5)(3,5)、( 4,2)(4,2) 、( 5,3)(5,3)。

    其联合权值分别为2 、15、2 、20、15、20。其中最大的是20,总和为74。

    【数据说明】

    对于30%的数据,1 < n leq 1001<n100;

    对于60%的数据,1 < n leq 20001<n2000;

    对于100%的数据,1 < n leq 200000, 0 < W_i leq 100001<n200000,0<Wi10000。

    保证一定存在可产生联合权值的有序点对。

    这一道题还是比较简单的

    本蒟蒻在考场上做加强版的(距离为4)得了70分(T了3个点 太菜了)

    拿到这上面来秒A!

    这一道题是用O(n)的时间复杂度来实现的 

    就是我们所说的距离为二

    仔细想想就是一个节点的两个儿子(就是和他相连的那些节点 这里就简称儿子吧)

    我们只需要枚举每一个节点 把它的两个儿子相乘求和 并纪录最大即可

    但是!

    这一道题并没有规定有几个儿子

    所以假如有3个儿子咋办?或者更多的儿子?

    还是比较简单的 在有3个儿子的时候 我们所要求的就是2ab+2bc+2ab 那么这个式子可以变形为什么呢?

    就是(a+b+c)^2-(a^2+b^2+c^2)神不神奇

    也就是说针对每一个节点我们只需要记录一个和的平方 一个平方的和就行了

    但是这一道题还要注意一下取模(考场上差点因为这个从70变成10)

    就是你在用ans1*ans1%mod-ans2%mod的时候 可曾想过这会不会是一个负数 也就是说ans2>ans1*ans1 额。。

    我们就这样:

    ((ans1*ans1%mod-ans2%mod)+mod)%mod

    同时40分wa的小盆友们 注意题目只让对和取模偶 没有让对乘积的最大值取模

    对了 仿佛还没有说最大乘积 这个也是非常的简单啊

    就是也是在枚举每一个点的过程中 把它儿子里最大的和次大的记录下来 乘一下不就是了吗。。。(好无聊的问题)

    下面上参差不齐的代码

    #include<bits/stdc++.h>
    #define maxn 200005
    #define mod 10007
    using namespace std;
    vector<int> v[maxn];
    int w[maxn];
    int main()
    {
        int n;scanf("%d",&n);
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            v[x].push_back(y);v[y].push_back(x);
        }
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        long long ans1=0,ans2=0,Maxans=-1,ans=0;
        for(int i=1;i<=n;i++){
            long long Max=0,Sec=0;
            for(int j=0;j<v[i].size();j++){
                ans1=(ans1+w[v[i][j]])%mod;
                ans2=(ans2+w[v[i][j]]*w[v[i][j]]%mod)%mod;
                if(w[v[i][j]]>Max)
                    Sec=Max,Max=w[v[i][j]];
                else if(w[v[i][j]]>Sec) Sec=w[v[i][j]];
            }
            Maxans=max(Maxans,Max*Sec);
            ans=(ans+(ans1*ans1%mod-ans2%mod)%mod+mod)%mod;
            ans1=ans2=0;
        }
        printf("%lld %lld",Maxans,ans%mod);
        
    }
    //link
    /*
    首先这一道题的思路还是比较清晰的
    就是说和的平方-平方的和
    但是很难找出那些端点
    怎么找端点呢?
    我们可以枚举每一个节点!
    这个节点的左儿子的儿子和右儿子的儿子相差4
    
    那么我们再来理清一下思路
    就是说枚举每一个节点 把它儿子的儿子们全都记上就行了
    看起来好像很简单的样子耶 但是有一个前提 就是说你所枚举的那一个点必须有至少两个孙子 而且还不是同一个儿子生的。。
    但是这样子说回来又不太对了。。
    
    不对 上面的否决
    偶 忽然想到了什么 
    要每一个当前枚举的节点的儿子的权值和去相乘  把每一个儿子的儿子们组合成一个整体。。。 偶!
    那么倘若真的是这个样子 那么时间复杂度就是 
    O(nlogn)反正感觉这样子就 不会超时了 啊哈哈哈哈哈哈哈哈 
     
    前途一片光明!
    只要俺把这一道题卡出来 就哈哈哈哈哈哈了
    加油!还有2个小时 你能行! 
    */ 
    #include<bits/stdc++.h>
    #define maxn 200005
    using namespace std;
    const long long mod=10007;
    int n;
    vector<int> v[maxn];//邻接表
    int w[maxn]; 
    inline int read()
    {
        int X=0; bool flag=1; char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
        while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
        if(flag) return X;
        return ~(X-1);
    }
    long long Get(int rt,int fa){//枚举孙子 求和 
        int y;long long Retans=0;
        for(int i=0;i<v[rt].size();i++){
            y=v[rt][i];if(y!=fa) Retans=(Retans+w[y])%mod;
            
        }
        return Retans%mod;
    }
    int main()
    {
        freopen("link.in","r",stdin);
        freopen("link.out","w",stdout); 
        scanf("%d",&n);int x,y;
        for(int i=1;i<=n-1;i++){
            x=read(),y=read();//1.读入结束
            v[x].push_back(y);v[y].push_back(x);    }
        for(int i=1;i<=n;i++) w[i]=read(),w[i]%=mod;
        //2.开始枚举啊
        long long ans1=0;//和的平方
        long long ans2=0;//平方的和 
        for(int i=1;i<=n;i++){//枚举每一个中转点 
            long long ans11=0,ans22=0,t=0;
            if(v[i].size()>=2)
                for(int j=0;j<v[i].size();j++){//枚举儿子 
                    long long AnsGrand=Get(v[i][j],i);
                    if(AnsGrand!=0) t++;
                    ans11=(ans11+AnsGrand)%mod;
                    ans22=(ans22+AnsGrand*AnsGrand)%mod;
                }
            if(t>=1){
            ans1=(ans1+ans11*ans11)%mod;
            ans2=(ans2+ans22)%mod;}
            
        }
        ans1%=mod;
        ans2%=mod;
        //ans1=(ans1*ans1)%mod;
        long long ans=((ans1-ans2)%mod+mod)%mod; 
        printf("%lld",ans);
        
        
        return 0;
    }/*
    8
    1 2
    1 3
    3 4
    4 5
    4 6
    6 7
    7 8
    8 2 6 7 9 3 2 10
    1048
    */
    (距离为4)考场绝望70分代码 不必瞅
  • 相关阅读:
    持续集成(转)
    Java中前台JSP请求Servlet实例(http+Servlet)
    Java中Map集合的四种访问方式(转)
    Python中字符串操作
    Python中的range函数用法
    Python学习资料下载地址(转)
    Linux性能工具介绍
    性能问题定位方法
    录制脚本前需要理解的几个基本概念
    Python 硬件底层基础理论
  • 原文地址:https://www.cnblogs.com/Tidoblogs/p/11625734.html
Copyright © 2020-2023  润新知