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 \times 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分代码 不必瞅

 

 

posted @ 2019-10-05 20:59  DreamingBligo_Tido  阅读(118)  评论(0编辑  收藏  举报