[IOI2011]Race

Posted by Dispwnl on July 12, 2018

题目

题目描述

给一棵树,每条边有权。求一条简单路径,权值和等于$K$,且边的数量最小。

输入输出格式

输入格式:

第一行:两个整数$n,k$。

第二至$n$行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从$0$开始)。

输出格式:

一个整数,表示最小边数量。

如果不存在这样的路径,输出$-1$。

输入输出样例

输入样例#1:

4 3
0 1 1
1 2 2
1 3 4

输出样例#1:

2

说明

$n\leq 200000,K\leq 1000000$。

题解

你会不会淀粉质啊?你学了个假的淀粉质吧,真丢人你褪裙退群吧 ————来着Aufun的嘲讽Orz

找路径长度为$k$,肯定用点分治搞

我的思路是找出所有的$k$长路径,随便记录边数最小值

记录点到根的距离时随便记录点到根的边数

在二分判断是否可行就行了

判断$-1$直接记录树中是否有小于等于$k$的路径

然后得了$80$……

冷静分析,发现判断不了两条边在同一子树里的情况

于是把每棵子树染色,判断边的时候判断是否不在同一子树里

然后……$75$?

奥忘清零了QAQ

然后发现处理不了链的情况,发现判断时出现相同长度的边就可能漏过答案

于是加了个枚举,枚举相同长度的边

终于AC了……复杂度应该比正常做法大一点枚举,开$O2$跑了$5s$多

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=2e5+1;
struct p{
	int x,y,dis;
}c[MAX<<1];
struct q{
	int x,d,id;
	bool operator< (const q &a)
	const{
		if(x!=a.x) return x<a.x;
		return d<a.d;
	}
}d[MAX];
int n,num,cnt,k,rt,Siz,ans,Ans=1e9;
int h[MAX],siz[MAX],f[MAX],id[MAX],sum[MAX];
bool use[MAX];
int read()
{
	int x=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void add(int x,int y,int dis)
{
	c[++num]=(p){h[x],y,dis},h[x]=num;
	c[++num]=(p){h[y],x,dis},h[y]=num;
}
int max(int x,int y)
{
	return x>y?x:y;
}
int look(int l,int x)
{
	int ans=0,r=cnt;
	while(l<=r)
	{
		int mid(l+r>>1);
		if(d[mid].x<x) l=mid+1;
		else ans=mid,r=mid-1;
	}
	return ans;
}
int look2(int l,int x)
{
	int ans=0,r=cnt;
	while(l<=r)
	{
		int mid(l+r>>1);
		if(d[mid].x<=x) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
void GET_NUM(int x,int fa,int D,int d1)
{
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if( y == fa || use[y] ) continue;
	  	d[++cnt]=(q){D+c[i].dis,d1+1,y};
	  	if(fa) id[y]=id[x];
	  	GET_NUM(y,x,d[cnt].x,d[cnt].d);
	  }
}
int GET_ANS(int x,int D,int d1)
{
	d[cnt=1]=(q){D,d1,x};
	int col=0;
	id[x]=0;
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if(use[y]||use[y]) continue;
	  	id[y]=++col;
	  }
	GET_NUM(x,0,D,d1);
	sort(d+1,d+1+cnt);
	int l=1,ans=0;
	while(d[l].x+d[cnt].x<k&&l<cnt) ++l;
	while(l<cnt&&k-d[l].x>=d[l].x)
	{
		int D3,D1(look(l+1,k-d[l].x)),D2(look2(l+1,k-d[l].x));
		D3=D1; 
		for(;id[d[D3].id]==id[d[l].id]&&D3<=cnt;++D3)
		  if(d[D3].x!=k-d[l].x) break;
		if(!D&&d[D3].x==k-d[l].x)
		{
			if(id[d[l].id]!=id[d[D3].id])
			Ans=min(Ans,d[l].d+d[D3].d);
		}
		if(D2>=D1) ans+=D2-D1+1;
		++l;
	}
	return ans;
}
void GET_ROOT(int x,int fa)
{
	siz[x]=1,f[x]=0;
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if(y==fa||use[y]) continue;
	  	GET_ROOT(y,x);
	  	siz[x]+=siz[y],f[x]=max(f[x],siz[y]);
	  }
	f[x]=max(f[x],Siz-siz[x]);
	if(f[x]<f[rt]) rt=x;
}
void dfs(int x)
{
	use[x]=1,ans+=GET_ANS(x,0,0);
	for(int i=h[x];i;i=c[i].x)
	  {
	  	int y=c[i].y;
	  	if(use[y]) continue;
	  	ans-=GET_ANS(y,c[i].dis,1);
	  	Siz=siz[y],rt=0;
	  	GET_ROOT(y,x),dfs(rt);
	  }
}
int main()
{
	n=read(),k=read();
	for(int i=1;i<n;++i)
	  {
	  	int x=read()+1,y=read()+1,dis=read();
	  	add(x,y,dis);sum[i]=sum[i-1]+dis;
	  }
	f[rt]=Siz=n;
	GET_ROOT(1,0),dfs(1);
	printf("%d",ans?Ans:-1);
	return 0;
}