P1351 [NOIP2014 提高组] 联合权值 洛谷

0x10 题目链接

p1351

0x20 题目

0x21 Tag

图论,数学

0x22 题目描述

在这里插入图片描述

0x30 思路与算法

当然最大联合权值比较好求, 只需要求出当前节点cur四周点的最大值和次大值。
对于联合权值之和, 假设当前节点cur所连接的点的权值为 v 1 , v 2 , ⋯ v_1,v_2,⋯ v1,v2,那么节点cur对权值之和产生的贡献为: W = ∑ v i ∑ v j v i v j W=\sum_{v_i}\sum_{v_j}v_iv_j W=vivjvivj = ∑ v i v i ∑ v j ! = v i v j =\sum_{v_i}v_i\sum_{v_j!=v_i}v_j =vivivj!=vivj = ∑ v i v i [ ( ∑ v j v j ) − v i ] =\sum_{v_i}v_i[(\sum_{v_j}v_j)-v_i] =vivi[(vjvj)vi] = ( ∑ v i v i ) 2 − ∑ v i v i 2 =(\sum_{v_i}v_i)^2-\sum_{v_i}v_i^2 =(vivi)2vivi2
将每个节点的W加和即得联合权值之和。

0x40 代码

0x41 实现细节

声明

const int MAXN=2e5+5;
int ans1=0,ans2=0;
int a[MAXN];
int n;

这里ans1记录联合权值最大值,ans2记录联合权值之和。n是节点数,a数组存储节点权值。
存图

struct Edge
{
    int to, w, next;
}edges[MAXN<<1];
int heads[MAXN], cnt; // cnt为当前边的编号
inline void add(int from, int to, int w = 1)
{
    edges[++cnt].w = w;    //新增一条编号为cnt+1的边,边权为w
    edges[cnt].to = to;    //该边的终点为to
    edges[cnt].next = heads[from];  //把下一条边,设置为当前起点的第一条边
    heads[from] = cnt;  //该边成为当前起点新的第一条边
}

由于不知道根节点,存一个无向图方便dfs
DFS

void dfs(int cur,int fath)
{
    if(cur>n) return;
    int m1=0,m2=0;
    int sq=0,sum=0;

    for (int eg = heads[cur]; eg != 0; eg = edges[eg].next)
    {
        int to=edges[eg].to;
        if(to!=fath) dfs(to,cur);

        if(a[to]>m2)
        {
            if(a[to]>m1)
            {
                m2=m1;
                m1=a[to];
            }
            else m2=a[to];
        }
        sq+=((a[to]%MOD)*(a[to]%MOD))%MOD;
        sq%=MOD;
        sum+=a[to]%MOD;
        sum%=MOD;
    }
    ans1=max(ans1,m1*m2);
    ans2+=((((sum%MOD)*(sum%MOD))%MOD-sq%MOD)+MOD)%MOD;
    ans2%=MOD;
}

其中的if(cur>n) return;if(to!=fath) dfs(to,cur);防止反复DFS。两个if判断最小值和次小值。注意这里的MOD,做减法要在最后加MOD,如果开了long long 就不用每一项都模了。

0x42 完整代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 10007;
const ll INF = 0x3f3f3f3f;
// <------------------------------->
#define endl "\n"
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define HACK freopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#define RT double rtime = 1.0 * clock() / CLOCKS_PER_SEC;cout<<"\nRuntime: "<<rtime<< " s.\n";
#define debug(x) cout<<"target is "<<x<<endl
#define debug_arr(arr) for(auto x:arr) cout<<x<<" "; cout<<"\n";
// <------------------------------->
const int MAXN=2e5+5;
int ans1=0,ans2=0;
int a[MAXN];
int n;
struct Edge
{
    int to, w, next;
}edges[MAXN<<1];
int heads[MAXN], cnt; // cnt为当前边的编号
inline void add(int from, int to, int w = 1)
{
    edges[++cnt].w = w;    //新增一条编号为cnt+1的边,边权为w
    edges[cnt].to = to;    //该边的终点为to
    edges[cnt].next = heads[from];  //把下一条边,设置为当前起点的第一条边
    heads[from] = cnt;  //该边成为当前起点新的第一条边
}
//<------------------------------->
void dfs(int cur,int fath)
{
    if(cur>n) return;
    int m1=0,m2=0;
    int sq=0,sum=0;

    for (int eg = heads[cur]; eg != 0; eg = edges[eg].next)
    {
        int to=edges[eg].to;
        if(to!=fath) dfs(to,cur);

        if(a[to]>m2)
        {
            if(a[to]>m1)
            {
                m2=m1;
                m1=a[to];
            }
            else m2=a[to];
        }
        sq+=((a[to]%MOD)*(a[to]%MOD))%MOD;
        sq%=MOD;
        sum+=a[to]%MOD;
        sum%=MOD;
    }
    ans1=max(ans1,m1*m2);
    ans2+=((((sum%MOD)*(sum%MOD))%MOD-sq%MOD)+MOD)%MOD;
    ans2%=MOD;
}
int main()
{
    IOS;
    #ifdef LOCAL_JUDGE
    HACK;
    #endif
    
    cin>>n;
    for(int i=1;i<=n-1;++i)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;++i)
        cin>>a[i];
    dfs(1,0);
    cout<<ans1<<" "<<ans2;
    
    #ifdef LOCAL_JUDGE
    RT;
    #endif
    return 0;
}

0x50 另

代码仅代表个人答案。如有错误,请多指正。=)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值