原题链接
题意:给定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;
}