[BZOJ2599][IOI2011]Race
试题描述
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000
输入
第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)
输出
一个整数 表示最小边数量 如果不存在这样的路径 输出-1
输入示例
4 3 0 1 1 1 2 2 1 3 4
输出示例
2
数据规模及约定
见“试题描述”
题解
点分治裸题。我还调了半天TAT。。。好久没写什么都忘了。。。
每次找子树的中心往下递归处理,合并的时候开一个大小为 k 的数组 Cnt[i] 记录边权和为 i 时所需的最少边的条数,转移也很简单。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 200010 #define maxm 400010 #define maxk 1000010 #define oo 2147483647 int n, m, k, head[maxn], next[maxm], to[maxm], dist[maxm]; void AddEdge(int a, int b, int c) { to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; dist[m] = c; next[m] = head[a]; head[a] = m; return ; } bool vis[maxn]; int root, Siz, siz[maxn], f[maxn]; void getroot(int u, int fa) { siz[u] = 1; f[u] = 0; for(int e = head[u]; e; e = next[e]) if(to[e] != fa && !vis[to[e]]) { getroot(to[e], u); siz[u] += siz[to[e]]; f[u] = max(f[u], siz[to[e]]); } f[u] = max(f[u], Siz - siz[u]); if(f[u] < f[root]) root = u; return ; } int ToT, d[maxn], dep[maxn], Cnt[maxk], mark[maxk], ans; void dfs(int u, int fa, int dis, int depth) { d[++ToT] = dis; dep[ToT] = depth; for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != fa && dis + dist[e] <= k) dfs(to[e], u, dis + dist[e], depth + 1); return ; } void solve(int u) { // printf("%d ", u); vis[u] = 1; bool flag = 0; for(int e = head[u]; e; e = next[e]) if(!vis[to[e]]) { ToT = 0; dfs(to[e], u, dist[e], 1); // printf("%d: ", to[e]); for(int i = 1; i <= ToT; i++) printf("%d ", d[i]); putchar(' '); for(int i = 1; i <= ToT; i++) if(d[i] == k) ans = min(ans, dep[i]); if(flag) { for(int i = 1; i <= ToT; i++) if(d[i] < k && mark[k-d[i]] == u) ans = min(ans, dep[i] + Cnt[k-d[i]]); } for(int i = 1; i <= ToT; i++) if(d[i] < k) { if(mark[d[i]] != u) Cnt[d[i]] = dep[i], mark[d[i]] = u; else Cnt[d[i]] = min(Cnt[d[i]], dep[i]); } flag = 1; } for(int e = head[u]; e; e = next[e]) if(!vis[to[e]]) { f[0] = n + 1; Siz = siz[to[e]]; root = 0; getroot(to[e], u); solve(root); } return ; } int main() { // freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); n = read(); k = read(); for(int i = 1; i < n; i++) { int a = read() + 1, b = read() + 1, c = read(); AddEdge(a, b, c); } f[0] = n + 1; Siz = n; root = 0; getroot(1, 0); ans = oo; solve(root); if(ans < oo) printf("%d ", ans); else puts("-1"); return 0; }
IOI 居然有这么水的题!难道是因为年份太早了?!