Shortest-path tree HDU - 4871最小路径生成树+点分治

原题链接

题意:给定n个点,m条边。要求在图中找到一棵树,使得所有点到1的距离最短,然后给定k个点,要求经过k个点的路程最长,并求出最长的有几条。

分析:转换一下题意,大概分为两个子问题
1.求出关于1节点的最小路径生成树
2.求出经过k-1条边的路径的最长距离和条数

对于问题1,我们可以先跑一边最短路,求出所有点的最短路,然后从1开始遍历节点,如果dis[v] = dis[u] + w,那么说明这条边是合法的,我们就把它连上,因为要求按照字典序最小,所以我们把所有的边按照节点标号sort一下就搞定。

对于问题2,我们用ans来存最终的答案,用一个桶maxx[i]代表边数为i的路径的最长长度,用c[i]代表最大长度的路径有几条,其余按照点分板子来,注意一下细节问题即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <bitset>
#include <map>
//#define ACM_LOCAL
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e4 + 5, M = 1e5 + 5, INF = 0x3f3f3f3f;
struct Edge {
    int to, next;
    ll vi;
} e[N << 1];

int h[N], cnt;
int n, m, k;
void add(int u, int v, ll w) {
    e[cnt].to = v;
    e[cnt].vi = w;
    e[cnt].next = h[u];
    h[u] = cnt++;
}
struct Node {
    vector<PII> g[N];
    int vis[N];
    ll dis[N];
    void init() {
        for (int i = 1; i <= n; i++) g[i].clear();
    }

    struct node {
        int now; ll d;
        bool operator < (const node &rhs) const {
            return d > rhs.d;
        }
    };

    void dij(int st) {
        memset(dis, 0x3f, sizeof dis);
        memset(vis, 0, sizeof vis);
        dis[st] = 0; priority_queue<node> q;
        q.push({st, dis[st]});
        while (q.size()) {
            int u = q.top().now;
            q.pop();
            if (vis[u]) continue;
            vis[u] = 1;
            for (auto item : g[u]) {
                int v = item.first;
                int w = item.second;
                if (dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    if (!vis[v]) {
                        q.push({v, dis[v]});
                    }
                }
            }
        }
    }
}Dij;
int tap[N];
void build_tree(int x) {
    tap[x] = 1;
    for (auto item : Dij.g[x]) {
        int v = item.first;
        int w = item.second;
        if (tap[v]) continue;
        if (Dij.dis[v] == Dij.dis[x] + w) {
            add(x, v, w), add(v, x, w);
            build_tree(v);
        }
    }
}
int rt, sum;
int sz[N], mx[N], vis[N];
ll maxx[N], ans;
void getroot(int x, int fa) {
    sz[x] = 1, mx[x] = 0;
    for (int i = h[x]; ~i; i = e[i].next) {
        int y = e[i].to;
        if (vis[y] || y == fa) continue;
        getroot(y, x);
        sz[x] += sz[y];
        mx[x] = max(mx[x], sz[y]);
    }
    mx[x] = max(mx[x], sum - sz[x]);
    if (mx[x] < mx[rt]) rt = x;
}

ll d[N], ed[N], num, c[N];

void getd(int x, int fa, ll dis, ll edge) {
    if (edge > k) return;
    d[++d[0]] = dis;
    ed[d[0]] = edge;
    for (int i = h[x]; ~i; i = e[i].next) {
        int y = e[i].to;
        if (vis[y] || y == fa) continue;
        getd(y, x, dis + e[i].vi, edge+1);
    }
}

int cal(int x) {
    queue<int> que;
    for (int i = h[x]; ~i; i = e[i].next) {
        int y = e[i].to;
        if (vis[y]) continue;
        d[0] = 0;
        c[0] = 1;
        getd(y, x, e[i].vi, 1);
        for (int j = 1; j <= d[0]; j++) {
            if (k < ed[j]) continue;
            if (maxx[k - ed[j]] + d[j] == ans) num += c[k-ed[j]];
            else if (maxx[k - ed[j]] + d[j] > ans) num = c[k-ed[j]], ans = maxx[k - ed[j]] + d[j];
        }
        for (int j = 1; j <= d[0]; j++) {
            que.push(ed[j]);
            if (maxx[ed[j]] < d[j]) c[ed[j]] = 1, maxx[ed[j]] = d[j];
            else if (maxx[ed[j]] == d[j]) c[ed[j]]++;
        }
    }
    while (que.size()) maxx[que.front()] = -INF, c[que.front()] = 0, que.pop();
}

void work(int x) {
    maxx[0] = 0;
    cal(x);
    vis[x] = 1;
    for (int i = h[x]; ~i; i = e[i].next) {
        int y = e[i].to;
        if (vis[y]) continue;
        sum = sz[y], rt = 0;
        getroot(y, -1);
        work(rt);
    }
}
void solve() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d %d", &n, &m, &k);
        k--;
        Dij.init();
        for (int i = 1; i <= m; i++) {
            int u, v, w; scanf("%d %d %d", &u, &v, &w);
            Dij.g[u].push_back(PII{v, w});
            Dij.g[v].push_back(PII{u, w});
        }
        Dij.dij(1);
        for (int i = 1; i <= n; i++)
            sort(Dij.g[i].begin(), Dij.g[i].end());
        memset(h, -1, sizeof h);
        memset(vis, 0, sizeof vis);
        memset(tap, 0, sizeof tap);
        cnt = 0;
        build_tree(1);

        sum = n, rt = 0, mx[0] = INF, ans = 0;
        for (int i = 1; i <= n; i++) maxx[i] = -INF;
        getroot(1, 0);
        work(rt);
        printf("%lld %lld\n", ans, num);
    }
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值