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;
}
posted @ 2018-04-10 10:41  嘒彼小星  阅读(116)  评论(0编辑  收藏