码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4016 [FJOI2014]最短路径树问题

时间:2018-03-26 10:44:38      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:pos   math   efi   简单   包含   其他   markdown   返回   需要   

Description

给一个包含 \(n\) 个点, \(m\) 条边的无向连通图。从顶点 \(1\) 出发,往其余所有点分别走一次并返回。往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径 \(A\)\(1,32,11\) ,路径 \(B\)\(1,3,2,11\) ,路径 \(B\) 字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。

可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含 \(K\) 个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?

这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点 \(A\) 到点 \(B\) 的路径和点 \(B\) 到点 \(A\) 视为同一条路径。

Input

第一行输入三个正整数 \(n,m,K\) ,表示有 \(n\) 个点 \(m\) 条边,要求的路径需要经过 \(K\) 个点。

接下来输入 \(m\) 行,每行三个正整数 \(A_i,B_i,C_i(1\le A_i,B_i\le n,1\le C_i\le 10000)\) ,表示 \(A_i\)\(B_i\) 间有一条长度为 \(C_i\) 的边。数据保证输入的是连通的无向图。

Output

输出一行两个整数,以一个空格隔开,第一个整数表示包含 \(K\) 个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

Sample

Sample Input

6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1

Sample Output

3 4

Solution

首先肯定要把最短路树给建出来。

然后就是点分傻逼题。 \(g[0][i]\) 表示包含 \(i\) 个点的路径的最大长度, \(g[1][i]\) 表示方案数。大家都会。

这题出得不太好啊,最短路树的那个完全是恶意增加题目代码复杂度。我还真不信会点分治的人不会构造最短路树……

不过这几天沉迷字符串,来一道点分写写也是不错的。

#include<bits/stdc++.h>
using namespace std;

#define N 30001
#define ll long long

inline int read() {
    int x = 0; char ch = getchar(); while (!isdigit(ch))  ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x;
}

struct edge { int v, w, next; }e[N << 2], et[N << 2];
int n, m, K, head[N], tot = 1, headt[N], tott = 1, dis[N], from[N], siz[N], mx[N], sum, root, g[2][N], ans1, ans2, dep[N], fa[N], q[N];
bool inq[N], vis[N];

inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
inline void addt(int u, int v, int w) { et[++tott].v = v, et[tott].w = w, et[tott].next = headt[u], headt[u] = tott; }

void spfa() {
    deque<int> q; q.push_back(1);
    for (int i = 2; i <= n; i++) dis[i] = 2000000000;
    ll sum = 0; int cnt = 1;
    while (!q.empty()) {
        int u = q.front(); q.pop_front();
        if ((ll)dis[u] * cnt > sum) { q.push_back(u); continue; }
        inq[u] = 0, cnt--, sum -= dis[u];
        for (int i = headt[u], v; i; i = et[i].next) if (dis[v = et[i].v] > dis[u] + et[i].w) {
            dis[v] = dis[u] + et[i].w, from[v] = i;
            if (!inq[v]) {
                if (q.empty() || dis[v] < dis[q.front()]) q.push_front(v);
                else q.push_back(v);
                inq[v] = 1, cnt++, sum += dis[v];
            }
        }
    }
    for (int i = 1; i <= n; i++) if (from[i])
        add(et[from[i] ^ 1].v, i, et[from[i]].w), add(i, et[from[i] ^ 1].v, et[from[i]].w);
}

void getRoot(int u, int fa) {
    siz[u] = 1, mx[u] = 0;
    for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v] && v != fa)
        getRoot(v, u), siz[u] += siz[v], mx[u] = max(mx[u], siz[v]);
    if ((mx[u] = max(mx[u], sum - siz[u])) < mx[root]) root = u;
}

void calc(int u) {
    for (int i = 1; i <= K; i++) g[0][i] = -2000000000, g[1][i] = 0;
    for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v]) {
        dep[v] = 1, dis[v] = e[i].w, fa[v] = u;
        int l = 1, r = 1; q[1] = v;
        while (l <= r) {
            int x = q[l++];
            for (int j = head[x], y; j; j = e[j].next) if (!vis[y = e[j].v] && y != fa[x])
                fa[y] = x, dep[y] = dep[x] + 1, dis[y] = dis[x] + e[j].w, q[++r] = y;
        }
        for (int j = 1; j <= r; j++) {
            int d = dep[q[j]]; fa[q[j]] = 0; if (d >= K) break;
            if (dis[q[j]] + g[0][K - d - 1] > ans1) ans1 = dis[q[j]] + g[0][K - d - 1], ans2 = g[1][K - d - 1];
            else if (dis[q[j]] + g[0][K - d - 1] == ans1) ans2 += g[1][K - d - 1];
        }
        for (int j = 1; j <= r; j++) {
            int d = dep[q[j]]; if (d >= K) break;
            if (d == K - 1) {
                if (dis[q[j]] > ans1) ans1 = dis[q[j]], ans2 = 1;
                else if (dis[q[j]] == ans1) ans2++;
            }
            else if (dis[q[j]] > g[0][d]) g[0][d] = dis[q[j]], g[1][d] = 1;
            else if (dis[q[j]] == g[0][d]) g[1][d]++;
        }
    }
}

void solve(int u) {
    calc(u), vis[u] = 1;
    for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v])
        root = 0, sum = siz[v], getRoot(v, 0), solve(root);
}

int main() {
    cin >> n >> m >> K;
    for (int i = 1, u, v, w; i <= m; i++) u = read(), v = read(), w = read(), addt(u, v, w), addt(v, u, w);
    spfa(), sum = mx[0] = n, getRoot(1, 0), solve(root);
    printf("%d %d", ans1, ans2);
    return 0;
}

bzoj4016 [FJOI2014]最短路径树问题

标签:pos   math   efi   简单   包含   其他   markdown   返回   需要   

原文地址:https://www.cnblogs.com/aziint/p/8648902.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!