bzoj-2599 Race

141 篇文章 0 订阅
88 篇文章 0 订阅
题意:
给出一棵n个点的树,每条边有一个权值;
求一条路径,权值和等于K,且边的数量最小;
n<=200000,K<=1000000;

题解:
没数据范围的坑爹题;
此题O(nlog^2n)是过不了的,要O(nlogn)的算法;
注意K的范围!

首先将树分治,答案一定在过根的某条链上;
那么统计子树之间的长度加和为K的链经过的边数;
因为K较小所以直接开一个数组记录长度为x的链最小经过了多少条边就可以了;
同样不能清数组,开个栈记录一下修改位置然后清掉;
因为每一层都是线性的所以时间复杂度O(nlogn);
树分治这东西真是太容易写挂了。。。

代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
#define pr pair<int,int>
using namespace std;
int next[N<<1],to[N<<1],val[N<<1],head[N],ce;
pr st[N];
int hash[1000100],K,ans,top;
bool ban[N];
void add(int x,int y,int v)
{
	to[++ce]=y;
	val[ce]=v;
	next[ce]=head[x];
	head[x]=ce;
}
int getS(int x,int pre)
{
	int size=1,i;
	for(i=head[x];i;i=next[i])
	{
		if(to[i]!=pre&&!ban[to[i]])
		{
			size+=getS(to[i],x);
		}
	}
	return size;
}
int getG(int x,int pre,int tot,int &g)
{
	int size=1,temp,i;
	bool flag=1;
	for(i=head[x];i;i=next[i])
	{
		if(to[i]!=pre&&!ban[to[i]])
		{
			temp=getG(to[i],x,tot,g);
			size+=temp;
			if(temp>(tot>>1))
				flag=0;
		}
	}
	if(tot-size>(tot>>1))
		flag=0;
	if(flag)
		g=x;
	return size;
}
void getP(int x,int pre,int dis,int cnt)
{
	st[++top]=pr(dis,cnt);
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=pre&&!ban[to[i]])
		{
			getP(to[i],x,dis+val[i],cnt+1);
		}
	}
}
void calc(int x)
{
	int temp,i,j;
	top=hash[0]=0;
	for(i=head[x];i;i=next[i])
	{
		if(!ban[to[i]])
		{
			temp=top;
			getP(to[i],0,val[i],1);
			for(j=temp+1;j<=top;j++)
				if(st[j].first<=K)
					ans=min(ans,hash[K-st[j].first]+st[j].second);
			for(j=temp+1;j<=top;j++)
				if(st[j].first<=K)
					hash[st[j].first]=min(hash[st[j].first],st[j].second);
		}
	}
	for(i=1;i<=top;i++)
		if(st[i].first<=K)
			hash[st[i].first]=0x3f3f3f3f;
}
void slove(int x,int tot)
{
	ban[x]=1;
	if(tot==1)	return ;
	calc(x);
	int g,i,s;
	for(i=head[x];i;i=next[i])
	{
		if(!ban[to[i]])
		{
			s=getS(to[i],0);
			getG(to[i],0,s,g);
			slove(g,s);
		}
	}
}
int main()
{
	int n,m,i,j,x,y,v;
	scanf("%d%d",&n,&K);
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&v);
		x++,y++;
		add(x,y,v),add(y,x,v);
	}
	memset(hash,0x3f,sizeof(hash));
	getG(1,0,n,x);
	ans=0x3f3f3f3f;
	slove(x,n);
	if(ans==0x3f3f3f3f)	ans=-1;
	printf("%d\n",ans);
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值