BZOJ 2599: [IOI2011]Race

首先我知道这是一道点分治可以过的题.

开始考虑普通的点分治套路,dis[],deep[],用来记录当前子树中到当前根的边权和和边的条数,用T[x]保存边权和为x时的最小边数

结果发现自己不太清楚怎么处理在同一棵子树的情况

仔细想想只要等这一棵子树已经遍历过了再更新T数组,而不是在遍历的过程中直接更新,就避免了统计同一子树下的点对(写完这句话我意识到我就是个傻13)


#include<cstdio>
#include<algorithm>
#define N 200005
#define M 1000005
#define INF 1e9
using namespace std;

int n,k,top,ans,pre,cnt=1,sum,root,head[N];
struct edge{int to,v,next;}e[N*2];
struct p{int deep,dis;}d[N];

inline int read()
{
	int a=0,f=1;static char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
	return a*f;
}

inline void add(int x,int y,int v)
{
	e[++cnt].next=head[x];
	head[x]=cnt;
	e[cnt].to=y,e[cnt].v=v;
}

int mx[N],son[N],dis[N],mark[N],deep[N],t[M];

void getr(int x,int fa)
{
	mx[x]=0,son[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		if(e[i].to==fa||mark[e[i].to]) continue;
		getr(e[i].to,x);
		son[x]+=son[e[i].to];
		mx[x]=max(mx[x],son[e[i].to]);
	}
	mx[x]=max(mx[x],sum-son [x]);
	if(mx[x]<mx[root]) root=x;
}

void getd(int x,int fa)
{
	int p=dis[x];
	if(p>k) return;
	d[++top].deep=deep[x],d[top].dis=dis[x];
	ans=min(ans,deep[x]+t[k-p]);
	for(int i=head[x];i;i=e[i].next)
	{
		if(mark[e[i].to]||e[i].to==fa) continue;
		dis[e[i].to]=dis[x]+e[i].v;
		deep[e[i].to]=deep[x]+1;
		if(dis[e[i].to]<=k)
			getd(e[i].to,x);
	}
}

void cal(int x)
{
	dis[x]=0,deep[x]=0;
	top=0,pre=1;
	for(int i=head[x];i;i=e[i].next)
	{
		if(!mark[e[i].to])
		{
			deep[e[i].to]=1;dis[e[i].to]=e[i].v;
			getd(e[i].to,x);
			for(int i=pre;i<=top;++i)
			{
				int p=d[i].dis;
				if(p<=k)
					t[p]=min(t[p],d[i].deep);
			}
			pre=top+1;
		}
	}
	for(int i=1;i<=top;++i)
	{
		int p=d[i].dis;
		if(p<=k) t[p]=INF;
	}
	t[0]=0;
}

void solve(int x)
{
	cal(x);mark[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		if(mark[e[i].to]) continue;
		sum=son[e[i].to];root=0;
		getr(e[i].to,0);
		solve(root);
	}
}

int main()
{
	n=read(),k=read();
	int i,x,y,v;
	for(i=1;i<n;++i)
	{
		x=read(),y=read(),v=read();
		++x,++y;
		add(x,y,v),add(y,x,v);
	}
	mx[0]=N,sum=n;ans=INF;
	for(int i=1;i<=k;++i) t[i]=INF;
	getr(1,0);
	solve(root);
	printf("%d\n",ans==INF?-1:ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值