POJ1987(树分治)

k的值很大答案应该用long long。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
#define maxn 111111
#define maxm 211111
#define INF 11111111
#define size Size

struct node {
    int from, to, next;
    long long w;
}edge[maxm];
int n, m, k, cnt, root;
long long ans;
int head[maxn];
bool vis[maxn];
int size[maxn], num[maxn]; //以i为根的子树的节点 i的子树中最多的节点
long long d[maxn], Min;

void add_edge (int from, int to, int w) {
    edge[cnt].from = from, edge[cnt].to = to, edge[cnt].w = w, edge[cnt].next = head[from],
    head[from] = cnt++;
}

void dfs_size (int u, int fa) {
    size[u] = 1;
    num[u] = 0;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (v == fa || vis[v])
            continue;
        dfs_size (v, u);
        size[u] += size[v];
        if (size[v] > num[u]) {
            num[u] = size[v];
        }
    }
}

void find_root (int u, int fa, int pre) { //找到重心
    long long cur = 0;
    cur = max (num[u], size[pre]-size[u]);
    if (cur < Min) {
        Min = cur;
        root = u;
    }
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (v == fa || vis[v])
            continue;
        find_root (v, u, pre);
    }
}

int tmp[maxn], tot;
void get_dis (int u, int fa, int dis) { //得到每个点到当前根的距离
    tmp[tot++] = dis;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (vis[v] || v == fa)
            continue;
        d[v] = dis+edge[i].w;
        get_dis (v, u, d[v]);
    }
}

int solve (int u, int d) {
    long long ans = 0;
    tot = 0;
    get_dis (u, 0, d);
    sort (tmp, tmp+tot);
    int r = tot-1, l = 0;
    while (r>l) {
        for (; tmp[r]+tmp[l] > k && r > l; r--) {}
        ans += r-l;
        l++;
    }
    return ans;
}

void dfs (int u) {
    Min = INF;
    dfs_size (u, 0);
    find_root (u, 0, u);
    vis[root] = 1;
    ans += solve (root, 0);
    for (int i = head[root]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if (vis[v])
            continue;
        ans -= solve (v, edge[i].w);
        dfs (v);
    }
}

int main () {
    //freopen ("in", "r", stdin);
    while (scanf ("%d%d", &n, &m) == 2) {
        cnt = ans = 0;
        int u, v, w;
        char op[10];
        memset (head, -1, sizeof head);
        for (int i = 1; i < n; i++) {
            scanf ("%d%d%d%s", &u, &v, &w, &op);
            add_edge (u, v, w);
            add_edge (v, u, w);
        }
        scanf ("%d", &k);
        memset (vis, 0, sizeof vis);
        dfs (1);
        printf ("%lld\n", ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值