[BZOJ2599][IOI2011]Race
[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\n", 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('\n');
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\n", ans);
else puts("-1");
return 0;
}
IOI 居然有这么水的题!难道是因为年份太早了?!

浙公网安备 33010602011771号