联合权值 noip2014 dfs

66 篇文章 0 订阅
55 篇文章 0 订阅

Description


给定n点n-1条边的连通图和各点的权值,统计所有距离为2的两点权值乘积之和与最大乘积

Solution


题意让我想起了幂萎的敌敌

首先要想到题目给的是一棵树,那么就相当于对所有相邻节点的权积求和

dfs的过程中枚举节点求积是会T的,于是考虑O(n)的方法。
已知 (a+b+c)2=a2+b2+c2+2ab+2ac+2bc
那么不难得到 2(a+b+c)=(a+b+c)2a2+b2+c2
显然可以记录一下搞出来

洛谷a了但是学校题库不行(汗,拿着幂萎的标就交了

Code


#include <stdio.h>
#include <vector>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, now) for (int i = ls[now]; i; i = e[i].next)
#define fill(x, t) memset(x, t, sizeof(x))
#define ll long long
#define pb push_back
#define MOD 10007
#define N 400001
#define E N * 4 + 1
#define L 30
using namespace std;
struct edge{int x, y, w, next;}e[E];
ll w[N], tot = 0, mx = 0;
int ls[N], d[N];
inline void addEdge(int &cnt, int x, int y, int w = 1){
    e[++ cnt] = (edge){x, y, w, ls[x]}; ls[x] = cnt;
}
inline ll max(ll x, ll y){
    return x > y ? x: y;
}
inline void dfs1(int now, int dep){
    d[now] = dep;
    erg(i, now){
        if (!d[e[i].y]){
            dfs1(e[i].y, dep + 1);
        }
    }
    vector<ll> v;
    erg(i, now){
        v.pb(e[i].y);
    }
    if (v.size()){
        ll sum = 0, sqs = 0;
        ll m1 = 0, m2;
        rep(i, 0, v.size() - 1){
            if (w[v[i]] > m1){
                m2 = m1;
                m1 = w[v[i]];
            }else if (w[v[i]] > m2){
                m2 = w[v[i]];
            }
            sum = (sum + w[v[i]]) % MOD;
            sqs = (sqs + w[v[i]] * w[v[i]] % MOD) % MOD;
        }
        ll ret = (sum * sum % MOD + MOD - sqs) % MOD;
        tot = (ret + tot) % MOD;
        mx = max(mx, m1 * m2);
    }
}
int main(void){
    int n;
    scanf("%d", &n);
    int edgeCnt = 0;
    rep(i, 1, n - 1){
        int x, y;
        scanf("%d%d", &x, &y);
        addEdge(edgeCnt, x, y);
        addEdge(edgeCnt, y, x);
    }
    rep(i, 1, n){
        scanf("%lld", &w[i]);
    }
    dfs1(1, 1);
    printf("%d %lld\n", mx, tot);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值