bzoj 2599: [IOI2011]Race(树的点分治)

2599: [IOI2011]Race

Time Limit: 70 Sec   Memory Limit: 128 MB
Submit: 3862   Solved: 1144
[ 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


http://blog.csdn.net/jaihk662/article/details/77429708

每次拉出重心,然后爆搜所有经过该节点的路径,看存不存在等于k的

#include<stdio.h>
#include<vector>
using namespace std;
typedef struct
{
	int y;
	int len;
}Point;
Point now, s[200005];
vector<Point> G[200005];
int ans, cnt, n, k, bet, heart, vis[200005], size[200005], dp[2000005], st[200005];
void Sech1(int u, int p)
{
	int i, v;
	size[u] = 1;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i].y;
		if(v==p || vis[v])
			continue;
		Sech1(v, u);
		size[u] += size[v];
	}
}
void Sech2(int u, int p, int &psiz)
{
	int bsiz, v, i;
	bsiz = psiz-size[u];
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i].y;
		if(v==p || vis[v])
			continue;
		bsiz = max(bsiz, size[v]);
		Sech2(v, u, psiz);
	}
	if(bsiz<bet)
	{
		bet = bsiz;
		heart = u;
	}
}
void Sech(int u, int p, int len, int now)
{
	int i, v;
	if(k-len>0 && dp[k-len]!=0 || k==len)
		ans = min(ans, now+dp[k-len]);
	if(k-len>0)
		s[++cnt].y = now, s[cnt].len = len;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i].y;
		if(vis[v] || v==p)
			continue;
		Sech(v, u, len+G[u][i].len, now+1);
	}
}
void Count(int u)
{
	int i, j, v, all;
	all = 0;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i].y;
		if(vis[v])
			continue;
		cnt = 0;
		Sech(v, u, G[u][i].len, 1);
		for(j=1;j<=cnt;j++)
		{
			if(dp[s[j].len]==0 && s[j].len!=0)
				dp[s[j].len] = s[j].y, st[++all] = s[j].len;
			else
				dp[s[j].len] = min(dp[s[j].len], s[j].y);
		}
	}
	if(dp[k]!=0)
		ans = min(ans, dp[k]);
	for(i=1;i<=all;i++)
		dp[st[i]] = 0;
}
void Divide(int u)
{
	int i, v;
	Sech1(u, 0);
	bet = 200005;
	Sech2(u, 0, size[u]);
	vis[heart] = 1;
	u = heart;
	Count(u);
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i].y;
		if(vis[v])
			continue;
		Divide(v);
	}
}
int main(void)
{
	int x, y, len, i;
	//freopen("in.txt", "r", stdin);
	scanf("%d%d", &n, &k);
	for(i=1;i<=n-1;i++)
	{
		scanf("%d%d%d", &x, &y, &len);
		x++, y++;
		now.y = y, now.len = len;
		G[x].push_back(now);
		now.y = x;
		G[y].push_back(now);
	}
	if(k==0)
	{
		printf("0\n");
		return 0;
	}
	ans = 2345667;
	Divide(1);
	if(ans==2345667)
		ans -= 2345668;
	printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值