BZOJ2599: [IOI2011]Race
Time Limit: 70 Sec Memory Limit: 128 MB
Submit: 4711 Solved: 1382
[Submit][Status][Discuss]
Description
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000
Input
第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)
Output
一个整数 表示最小边数量 如果不存在这样的路径 输出-1
Sample Input
4 3
0 1 1
1 2 2
1 3 4
Sample Output
2
HINT
2018.1.3新加数据一组,未重测
Source
题解
点分治,每一块,维护\(dis[x]\)表示重心到x距离,全局维护\(f[x]\)表示长度为\(x\)的路径最小点数。用\(dis\)更新\(f[x]\)的时候,只需要对于每一棵子树,统计完\(dis\)后更新f即可,这样保证两个点的路径一定被统计一次。
注意点编号从\(0\)开始,且边权有\(0\),因此初值\(f[0]=0,f[x]=INF,x\neq0\)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
inline int max(int a, int b){return a > b ? a : b;}
inline int min(int a, int b){return a < b ? a : b;}
inline void swap(int &x, int &y){int tmp = x;x = y;y = tmp;}
inline void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}
const int INF = 0x3f3f3f3f;
const int MAXN = 200000 + 10;
const int MAXK = 1000000 + 10;
struct Edge
{
int u, v, w, nxt;
Edge(int _u, int _v, int _w, int _nxt){u = _u, v = _v, w = _w, nxt = _nxt;}
Edge(){}
}edge[MAXN << 1];
int head[MAXN], cnt, root, vis[MAXN], sum, dp[MAXN], size[MAXN], f[MAXK], dis[MAXN], num[MAXN], ans, n, k;
inline void insert(int a, int b, int c){edge[++ cnt] = Edge(a, b, c, head[a]), head[a] = cnt;}
void dfs_dis(int x, int pre)
{
if(dis[x] <= k) ans = min(ans, num[x] + f[k - dis[x]]);
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v] || v == pre) continue;
dis[v] = edge[pos].w + dis[x], num[v] = num[x] + 1, dfs_dis(v, x);
}
}
int dfs_f(int x, int flag, int pre)
{
if(dis[x] <= k)
{
if(flag) f[dis[x]] = min(f[dis[x]], num[x]);
else f[dis[x]] = dis[x] == 0 ? 0 : INF;
}
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v] || v == pre) continue;
dfs_f(v, flag, x);
}
}
void dfs_dp(int x, int pre)
{
size[x] = 1, dp[x] = 0;
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v] || v == pre) continue;
dfs_dp(v, x), size[x] += size[v], dp[x] = max(dp[x], size[v]);
}
dp[x] = max(dp[x], sum - size[x]);
if(dp[x] < dp[root]) root = x;
}
void solve(int x)
{
root = 0;
dfs_dp(x, -1), x = root;
vis[x] = 1;
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v]) continue;
num[v] = 1, dis[v] = edge[pos].w;
dfs_dis(v, -1), dfs_f(v, 1, -1);
}
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v]) continue;
dfs_f(v, 0, -1);
}
for(int pos = head[x];pos;pos = edge[pos].nxt)
{
int v = edge[pos].v;
if(vis[v]) continue;
sum = size[v];
solve(v);
}
}
int main()
{
memset(f, 0x3f, sizeof(f)), f[0] = 0, ans = INF;
read(n), read(k);
for(int i = 1;i < n;++ i)
{
int tmp1, tmp2, tmp3;
read(tmp1), read(tmp2), read(tmp3);
++ tmp1, ++ tmp2;
insert(tmp1, tmp2, tmp3), insert(tmp2, tmp1, tmp3);
}
sum = n, dp[0] = INF, solve(1);
if(ans != INF) printf("%d", ans);
else printf("-1");
return 0;
}