【树形DP】HDU 4303 Hourai Jeweled

通道

题意:给一个树,边有颜色,点有权值。满足路径上没有两个相邻边同色的路径是好的路径,求好的路径的(路径上的点的权值和)的总和

思路:

边权排序,从任意一点开始深搜,每颗子树搜索完毕之后向上返回pair<可以延伸到该点且最后一条边与由父节点到该点的边颜色不同的gorgeous边的条数 , 所有这种边分数的总和>
每次深搜完一个子节点之后,增加的过这一点的gorgeous边的总分数为:
    之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的分数 +
    之前深搜的所有子节点向上返回的分数之和 * 当前子节点返回的边数 +
    之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的边数 * 当前点的权

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAX_N = 300007;

typedef long long ll;

struct Edge {
    int u, v, w, nxt;
    Edge () {
        
    }
    Edge (int _u, int _v ,int _w, int _n) {
        u = _u; v = _v; w = _w; nxt = _n;
    }
    bool operator < (const Edge &rhs) const {
        return w < rhs.w;
    }
};

int n;
int head[MAX_N], ecnt;
ll ans, val[MAX_N], num[MAX_N], sum[MAX_N];
Edge T[MAX_N], G[MAX_N << 1];

void Clear() {
    ecnt = ans = 0; memset(head, -1, sizeof head);
    memset(num, 0, sizeof num); memset(sum, 0, sizeof sum);
}

void add(int u, int v, int w) {
    G[ecnt] = Edge(u, v, w, head[u]);
    head[u] = ecnt++;
}

void dfs(int u, int fa, int col) {
    ll tot = 0, tot1 = 0, tot2 = 0, k = 0, k1 = 0, k2 = 0;
    int nowCol = 0;
    for (int i = head[u]; ~i; i = G[i].nxt) {
        int v = G[i].v, w = G[i].w;
        if (v == fa) continue;
        dfs(v, u, w);
        if (w != col) tot += sum[v], k += num[v];
        if (w != nowCol) {
            tot1 += tot2; k1 += k2;
            tot2 = sum[v], k2 = num[v];
            nowCol = w;
        } else tot2 += sum[v], k2 += num[v];
        ans += sum[v] * (k1 + 1) + tot1 * num[v] + val[u] * (k1 + 1) * num[v];
    }
    num[u] = ++k, sum[u] = val[u] * k + tot;
}

template <class T>
inline bool rd(T &ret) {
    char c; int sgn;
    if(c = getchar() , c == EOF) return false;
    while(c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while(c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return true;
}

int main() {
    while (1 == scanf("%d", &n)) {
        for (int i = 1; i <= n; ++i) rd(val[i]);
        for (int i = 0; i + 1 < n; ++i) {
            rd(T[i].u), rd(T[i].v), rd(T[i].w);
        }
        sort(T, T + n - 1);
        Clear();
        for (int i = 0; i + 1 < n; ++i) {
            add(T[i].u, T[i].v, T[i].w);
            add(T[i].v, T[i].u, T[i].w);
        }
        dfs(1, -1, -1);
        printf("%I64d\n", ans);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Rojo/p/4718323.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值