题目描述
给一棵树,每条边有权。求一条简单路径,权值和等于 KKK,且边的数量最小。
输入格式
第一行包含两个整数 n,Kn, Kn,K。
接下来 n−1n - 1n−1 行,每行包含三个整数,表示一条无向边的两端和权值。
注意点的编号从 000 开始。
输出格式
输出一个整数,表示最小边数量。
如果不存在这样的路径,输出 −1-1−1。
输入输出样例
输入 #1复制
4 3
0 1 1
1 2 2
1 3 4
输出 #1复制
2
说明/提示
保证 n⩽2×105,n \leqslant 2 \times 10^5,n⩽2×105, K⩽106K \leqslant
题目大意 : 输入一棵树,如果存在两点之间的路径 == k, 那么输出该路径的最小边数, 不存在输出-1
思路 : 1e5次方处理树上两点之间的问题, 很显然是点分治, 对于这道题只要记录一下每个点到根经过边的就好, 这个在找
dis的过程中就可以记录, 并且判断是否存在K - 当前长度的边, 如果存在,就更新最小值, 注意如果当前的距离> K, 就没有必要再往下找了, 直接return。初始的时候所有长度的边数都是INF, 除了为0的(第一次遍历的是链, 如果单链就有到根距离 == k, 直接是当前到根的边数 + 0), 每次遍历完一棵树,就把相应的长度的数量清空, 用memset会超时
Accepted code
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & (-x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 2e6 + 100;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }
struct Edge
{
int v, w, next;
}e[MAXN << 1];
int head[MAXN], q[MAXN], maxp[MAXN], n, m, cnt;
int s_num[MAXN], cut[MAXN], line[MAXN], rt, rt_num, ans;
int dis[MAXN], res[MAXN], judge[MAXN];
bool vis[MAXN];
void init() {
MEM(head, -1); MEM(judge, INF); ans = INF;
}
void add(int from, int to, int wi) {
e[++cnt].v = to;
e[cnt].w = wi;
e[cnt].next = head[from];
head[from] = cnt;
}
void rt_dfs(int x, int fa) {
s_num[x] = 1, maxp[x] = 0;
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vi == fa || vis[vi]) continue;
rt_dfs(vi, x);
s_num[x] += s_num[vi];
Max(maxp[x], s_num[vi]);
}
Max(maxp[x], rt_num - s_num[x]);
if (maxp[x] < maxp[rt]) rt = x;
}
void dfs(int x, int fa, int step) {
if (dis[x] > m) return;
res[++res[0]] = dis[x];
Min(ans, judge[m - dis[x]] + step); // 存在两点之间路径经过根且长度为m
q[res[0]] = step;
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vi == fa || vis[vi]) continue;
dis[vi] = dis[x] + e[i].w;
dfs(vi, x, step + 1);
}
}
void Calc(int x) {
int tot = 0;
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vis[vi]) continue;
res[0] = 0, dis[vi] = e[i].w;
dfs(vi, x, 1);
for (int j = 1; j <= res[0]; j++) // 更新路径数量
line[++tot] = res[j], Min(judge[res[j]], q[j]);
}
for (int i = 1; i <= tot; i++) judge[line[i]] = INF; // 清空边的数量
}
void Solve(int x) {
vis[x] = true, judge[0] = 0; Calc(x); // 遍历子树
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vis[vi]) continue;
rt_num = s_num[vi]; maxp[rt = 0] = INF;
rt_dfs(vi, x); Solve(rt);
}
}
int main()
{
cin >> n >> m; init();
for (int i = 1; i < n; i++) {
int ui, vi, wi;
sc("%d %d %d", &ui, &vi, &wi);
ui++, vi++;
add(ui, vi, wi); add(vi, ui, wi);
}
rt_num = n, maxp[0] = INF; rt_dfs(1, 1);
Solve(rt);
if (ans == INF) cout << -1 << endl;
else cout << ans << endl;
return 0;
}