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;
}