BZOJ 4016: [FJOI2014]最短路径树问题

来源:互联网 发布:淘宝二类医疗器械备案 编辑:程序博客网 时间:2024/05/22 02:04

4016: [FJOI2014]最短路径树问题

Time Limit: 5 Sec  Memory Limit: 512 MB
Submit: 1602  Solved: 563
[Submit][Status][Discuss]

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行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。

Output

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

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



先建立出最短路径数,然后点分治可求。

f[i][0],表示当前深度为i的最大距离。

f[i][1],表示当前深度为i的最大距离数量。

最长路径和数量都可在分治过程中更新获得

#include<stdio.h>#include<string.h>#include<algorithm>#include<vector>#include<queue>using namespace std;const int maxm = 30005;struct node { int v, len, next;}edge[maxm * 4];struct H {int d, id; }dis[maxm];struct A{int w, num;}p[maxm], d[maxm];vector<node>V[maxm];int head[maxm * 4], vis[maxm], son[maxm], s[maxm], f[maxm][2], flag[maxm];int root, cnt, n, m, ans, sn, M, L, rev, tot;void add(int u, int v, int w){edge[cnt].v = v, edge[cnt].len = w, edge[cnt].next = head[u], head[u] = cnt++;edge[cnt].v = u, edge[cnt].len = w, edge[cnt].next = head[v], head[v] = cnt++;}void bfs(){node temp;for (int i = 1;i <= n;i++) dis[i].d = 0x3f3f3f3f, dis[i].id = -1;queue<int>q;dis[1].d = 0, dis[1].id = 0;q.push(1);while (!q.empty()){int u = q.front();q.pop();for (int i = head[u];i != -1;i = edge[i].next){int v = edge[i].v;if (dis[v].d > dis[u].d + edge[i].len || (dis[v].d == dis[u].d + edge[i].len&&dis[v].id > u)){V[v].clear();temp.v = u, temp.len = edge[i].len;V[v].push_back(temp);dis[v].d = dis[u].d + edge[i].len, dis[v].id = u;q.push(v);}}}}void work(int k){vis[k] = 1;for (int i = 0;i < V[k].size();i++){add(k, V[k][i].v, V[k][i].len);if (vis[V[k][i].v]) continue;work(V[k][i].v);}}void getroot(int k, int pre){son[k] = 1, s[k] = 0;for (int i = head[k];i != -1;i = edge[i].next){int v = edge[i].v;if (vis[v] || v == pre) continue;getroot(v, k);son[k] += son[v], s[k] = max(s[k], son[v]);}s[k] = max(s[k], sn - s[k]);if (s[root] > s[k]) root = k;}void getdep(int k, int pre){p[++tot] = d[k];for (int i = head[k];i != -1;i = edge[i].next){int v = edge[i].v;if (v == pre || vis[v]) continue;d[v].w = d[k].w + edge[i].len, d[v].num = d[k].num + 1;getdep(v, k);}}void query(int k,int w,int num){d[k].w = w, d[k].num = num;tot = 0, getdep(k, 0);for (int i = 1;i <= tot;i++){if (p[i].num < M){int len = p[i].w + f[M - p[i].num][0];if (len > L) L = len, ans = f[M - p[i].num][1];else if (len == L) ans += f[M - p[i].num][1];}else if (p[i].num == M){if (p[i].w > L) L = p[i].w, ans = 1;else if (p[i].w == L) ans++;}}for (int i = 1;i <= tot;i++){if (p[i].num <= M){if (p[i].w > f[p[i].num][0]) f[p[i].num][0] = p[i].w, f[p[i].num][1] = 1;else if (p[i].w == f[p[i].num][0]) f[p[i].num][1]++;flag[++rev] = p[i].num;}}}void dfs(int k){vis[k] = 1, rev = 0;for (int i = head[k];i != -1;i = edge[i].next){int v = edge[i].v;if (vis[v]) continue;query(v, edge[i].len, 1);}for (int i = 1;i <= rev;i++) f[flag[i]][1] = f[flag[i]][0] = 0;for (int i = head[k];i != -1;i = edge[i].next){int v = edge[i].v;if (vis[v]) continue;root = 0, sn = son[v];getroot(v, 0), dfs(root);}}int main(){int i, j, k, sum, x, y, z;scanf("%d%d%d", &n, &m, &M);memset(head, -1, sizeof(head)), cnt = 0, M--;for (i = 1;i <= m;i++){scanf("%d%d%d", &x, &y, &z);add(x, y, z);}bfs();memset(head, -1, sizeof(head)), cnt = 0;memset(vis, 0, sizeof(vis));for (i = 1;i <= n;i++)if (!vis[i]) work(i);memset(vis, 0, sizeof(vis));L = 0, ans = 0;root = 0, s[0] = 100000000, sn = n;getroot(1, 0), dfs(root);printf("%d %d\n", L, ans);return 0;}



原创粉丝点击