[LUOGU4149][IOI2011]Race
题目描述
给一棵树,每条边有权。求一条简单路径,权值和等于 KKK ,且边的数量最小。
输入输出格式
输入格式:第一行:两个整数 n,kn,kn,k 。
第二至 nnn 行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从 000 开始)。
输出格式:一个整数,表示最小边数量。
如果不存在这样的路径,输出 −1-1−1 。
输入输出样例
输入样例#1:
4 3 0 1 1 1 2 2 1 3 4
输出样例#1:
2
说明
n≤200000,K≤1000000n\le 200000,K\le 1000000n≤200000,K≤1000000 。
点分治, 记录路径长度为i的最小经过路径, 然后就是正常的点分治。
之后记得把上面说的数组赋成正无穷, 这个不能暴力清空, 否则复杂度退化。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; inline int read() { int res=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar(); return res; } #define reg register #define N 200005 int n, tot, k; struct edge { int nxt, to, val; }ed[N*2]; int head[N], cnt = 1; inline void add(int x, int y, int z) { ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } bool cut[N*2]; int siz[N], root, mrt = 1e9, maxx[N]; void dfs(int x, int fa) { siz[x] = 1; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa or cut[i]) continue; dfs(to, x); siz[x] += siz[to]; } } void efs(int x, int fa) { int tmp = tot - siz[x]; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa or cut[i]) continue; efs(to, x); tmp = max(tmp, siz[to]); } if (tmp < mrt) mrt = tmp, root = x; } inline int FindRoot(int x) { dfs(x, 0); mrt = 1e9; tot = siz[x]; root = n; efs(x, 0); return root; } int ans = 1e9; int dep[N], len[1000005], dis[N];//深度|长为i的最小深度|长度 void Work(int x, int fa) { if (dis[x] > k) return ; if (len[k - dis[x]] != 0x3f3f3f3f) if (dep[x] + len[k - dis[x]] <= ans) ans = dep[x] + len[k - dis[x]]; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (cut[i] or to == fa) continue; dep[to] = dep[x] + 1; dis[to] = dis[x] + ed[i].val; // len[dis[to]] = min(len[dis[to]], dep[to]); Work(to, x); } } void Pushup(int x, int fa, bool opt) { if (dis[x] > k) return; if (opt) len[dis[x]] = 0x3f3f3f3f; else len[dis[x]] = min(len[dis[x]], dep[x]); for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa or cut[i]) continue; Pushup(to, x, opt); } } void solve(int rt) { root = FindRoot(rt); len[0] = 0; //memset(len, 0x3f, sizeof len); for (reg int i = head[root] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (cut[i]) continue; dep[root] = 0, dep[to] = 1; dis[root] = 0, dis[to] = ed[i].val; Work(to, root); Pushup(to, root, 0); } for (reg int i = head[root] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (cut[i]) continue; Pushup(to, root, 1); } for (reg int i = head[root] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (cut[i]) continue; cut[i] = cut[i ^ 1] = 1; solve(to); } } int main() { n = read(), k = read(); for (reg int i = 1 ; i < n ; i ++) { int x = read(), y = read(), z = read(); add(x + 1, y + 1, z), add(y + 1, x + 1, z); } memset(len, 0x3f, sizeof len); solve(1); if (ans == 1e9) ans = -1; printf("%d\n", ans); return 0; }