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
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1
Sample Output
3 4
HINT
对于所有数据n<=30000,m<=60000,2<=K<=n。
数据保证最短路径树上至少存在一条长度为K的路径。
题解
看错题意,以为是求包含 $k$ 个点的简单路径共多少条。调了好久...
首先求字典序最小的最短路树,考虑将边拆成两条单向边,然后按终点从大到小排序,按序插入链式前向星中,保证找到的第一条最短路就是字典序最小的。
点分就比较裸了,记深度为 $i$ 时最大的路径长度为 $sum_i$ ,长度为 $sum_i$ ,且深度为 $i$ 的路径数为 $cnt_i$ 直接转移就好了。
1 //It is made by Awson on 2018.1.4 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define LD long double 17 #define Max(a, b) ((a) > (b) ? (a) : (b)) 18 #define Min(a, b) ((a) < (b) ? (a) : (b)) 19 using namespace std; 20 const int N = 30000; 21 const int M = 60000; 22 const int INF = ~0u>>1; 23 24 int n, m, k; 25 struct tt { 26 int to, next, cost; 27 }edge[(N<<1)+5]; 28 int path[N+5], top, ans1, ans2; 29 void add(int u, int v, int c) { 30 edge[++top].to = v; 31 edge[top].next = path[u]; 32 edge[top].cost = c; 33 path[u] = top; 34 } 35 36 namespace SPFA { 37 int a, b, c; 38 struct ss { 39 int u, v, c; 40 ss() {} 41 ss(int _u, int _v, int _c) { 42 u = _u, v = _v, c = _c; 43 } 44 bool operator < (const ss &b) const { 45 return v > b.v; 46 } 47 }w[(M<<1)+5]; 48 struct sss { 49 int to, next, cost; 50 }edge[(M<<1)+5]; 51 int path[N+5], top, tot, pre[N+5], vis[N+5], prec[N+5]; 52 LL dist[N+5]; 53 void add_e(int u, int v, int c) { 54 edge[++top].to = v; 55 edge[top].cost = c; 56 edge[top].next = path[u]; 57 path[u] = top; 58 } 59 void spfa() { 60 memset(dist, 127/3, sizeof(dist)); dist[1] = 0; 61 queue<int>Q; Q.push(1); vis[1] = 1; 62 while (!Q.empty()) { 63 int u = Q.front(); Q.pop(); vis[u] = 0; 64 for (int i = path[u]; i; i = edge[i].next) 65 if (dist[edge[i].to] > dist[u]+edge[i].cost) { 66 dist[edge[i].to] = dist[u]+edge[i].cost; 67 pre[edge[i].to] = u, prec[edge[i].to] = edge[i].cost; 68 if (!vis[edge[i].to]) { 69 vis[edge[i].to] = 1; Q.push(edge[i].to); 70 } 71 } 72 } 73 } 74 void main() { 75 scanf("%d%d%d", &n, &m, &k); 76 for (int i = 1; i <= m; i++) { 77 scanf("%d%d%d", &a, &b, &c); 78 w[++tot] = ss(a, b, c), w[++tot] = ss(b, a, c); 79 } 80 sort(w+1, w+1+tot); 81 for (int i = 1; i <= tot; i++) add_e(w[i].u, w[i].v, w[i].c); 82 spfa(); 83 for (int i = 2; i <= n; i++) add(pre[i], i, prec[i]), add(i, pre[i], prec[i]); 84 } 85 } 86 namespace Point_divide { 87 int size[N+5], mx[N+5], minsize, root, vis[N+5], cnt[N+5], sum[N+5]; 88 void get_size(int o, int fa) { 89 size[o] = 1, mx[o] = 0; 90 for (int i = path[o]; i; i = edge[i].next) 91 if (edge[i].to != fa && !vis[edge[i].to]) { 92 get_size(edge[i].to, o); 93 size[o] += size[edge[i].to]; 94 mx[o] = Max(mx[o], size[edge[i].to]); 95 } 96 } 97 void get_root(int o, int rt, int fa) { 98 mx[o] = Max(mx[o], size[rt]-size[o]); 99 if (mx[o] < minsize) minsize = mx[o], root = o; 100 for (int i = path[o]; i; i = edge[i].next) 101 if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, rt, o); 102 } 103 void get_ans(int o, int fa, int dep, int cost) { 104 if (dep >= k) return; 105 if (cnt[k-1-dep] && ans1 < cost+sum[k-1-dep]) ans1 = cost+sum[k-1-dep], ans2 = cnt[k-1-dep]; 106 else if (cnt[k-1-dep] && ans1 == cost+sum[k-1-dep]) ans2 += cnt[k-1-dep]; 107 for (int i = path[o]; i; i = edge[i].next) 108 if (edge[i].to != fa && !vis[edge[i].to]) get_ans(edge[i].to, o, dep+1, cost+edge[i].cost); 109 } 110 void get_update(int o, int fa, int dep, int cost) { 111 if (dep >= k) return; 112 if (sum[dep] < cost) sum[dep] = cost, cnt[dep] = 1; 113 else if (sum[dep] == cost) ++cnt[dep]; 114 for (int i = path[o]; i; i = edge[i].next) 115 if (edge[i].to != fa && !vis[edge[i].to]) get_update(edge[i].to, o, dep+1, cost+edge[i].cost); 116 } 117 void get_clean(int o, int fa, int dep) { 118 if (dep >= k) return; 119 cnt[dep] = 0, sum[dep] = 0; 120 for (int i = path[o]; i; i = edge[i].next) 121 if (edge[i].to != fa && !vis[edge[i].to]) get_clean(edge[i].to, o, dep+1); 122 } 123 void work(int o) { 124 minsize = INF; 125 get_size(o, 0), get_root(o, o, 0); 126 vis[root] = 1; cnt[0] = 1; 127 for (int i = path[root]; i; i = edge[i].next) 128 if (!vis[edge[i].to]) get_ans(edge[i].to, root, 1, edge[i].cost), get_update(edge[i].to, root, 1, edge[i].cost); 129 cnt[0] = 0; 130 for (int i = path[root]; i; i = edge[i].next) 131 if (!vis[edge[i].to]) get_clean(edge[i].to, root, 1); 132 for (int i = path[root]; i; i = edge[i].next) 133 if (!vis[edge[i].to]) work(edge[i].to); 134 } 135 void main() {work(1); } 136 } 137 void work() { 138 SPFA::main(); 139 Point_divide::main(); 140 printf("%d %d ", ans1, ans2); 141 } 142 int main() { 143 work(); 144 return 0; 145 }