IOI2011 Race

Description

给一棵有N(1 <= N <= 200000)个结点的树,每条边有权,求一条路径,权值和等于K(1 <= K <= 1000000)且边的数量最小。

Input

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

Output

输出仅一个整数,表示最小边数量,如果不存在这样的路径,则输出-1

Sample Input

4 3 0 1 1 1 2 2 1 3 4

Sample Output

2

刚做的时候发现水题一道啊,甚至不需要去重复:

inline void Calc(int x){
		DocDist(x,cnt=0,0,0);
		sort(a+1,a+1+cnt);
		int L=1,r=cnt;
		while(L<r){
			if(a[L].dis+a[r].dis==k)ans=min(ans,a[L].num+a[r].num),--r;
			if(a[L].dis+a[r].dis>k)--r;
			if(a[L].dis+a[r].dis<k)++L;
		}
	}

然后调了很久愣是不知道哪里错了,结果画了个图就gg了:

如果黑边+红边=k。。。还是太菜了,这个都想不到= =但上述写法居然可以水很多分

然而冥思苦想甚久,我毅然决然地做好了co标准备= =

其实想法是一样的= =不需要去重,我们只需统计子树之间的贡献就可以了,详情见代码:

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
const int N = 2e5+10;
struct Edge{
	int to[N<<1],next[N<<1],w[N<<1];
	int cnt,h[N];
	inline void add(int x,int y,int z){
		next[++cnt]=h[x];
		to[cnt]=y;
		w[cnt]=z;
		h[x]=cnt;
	}
}e;
struct edge{
	int dis,num;
	bool operator <(const edge&A)const{
		return dis<A.dis;
	}
}a[N];
int n,k;
struct PF{
	int Minsiz,siz[N];
	int G,sum,ans;
	bool vst[N];
	int cnt,num[N*5];
	inline void FindG(int x,int fa){//找重心 
		int Maxsiz=0;
		siz[x]=1;
		for(int p=e.h[x];p;p=e.next[p])if(e.to[p]^fa&&!vst[e.to[p]]){
			FindG(e.to[p],x);
			siz[x]+=siz[e.to[p]];
			Maxsiz=max(Maxsiz,siz[e.to[p]]);
		}
		Maxsiz=max(Maxsiz,sum-siz[x]);
		if(Maxsiz<Minsiz)G=x,Minsiz=Maxsiz;
	}
	inline void DocDist(int x,int fa,int dist,int num){
		a[++cnt]=(edge){dist,num};
		for(int p=e.h[x];p;p=e.next[p])if(e.to[p]^fa&&!vst[e.to[p]]){
			DocDist(e.to[p],x,dist+e.w[p],num+1);
		}
	}
	inline void Calc(int x){
		num[0]=cnt=0;
		for(int p=e.h[x];p;p=e.next[p])if(!vst[e.to[p]]){
			int tmp=cnt;
			DocDist(e.to[p],x,e.w[p],1);//我们以重心为根,统计距离 
			Inc(i,tmp+1,cnt)if(a[i].dis<=k)ans=min(ans,num[k-a[i].dis]+a[i].num);//注意这两个循环的顺序,显然如果先做下一个循环,就变成了之前(错误) 
			Inc(i,tmp+1,cnt)if(a[i].dis<=k)num[a[i].dis]=min(num[a[i].dis],a[i].num);//的思想,因为子树内部的贡献是会出问题的 
		}
		Inc(i,1,cnt)if(a[i].dis<=k)num[a[i].dis]=0x3f3f3f3f;
	}
	inline void stat(int x){
		Minsiz=1<<30;
		FindG(x,0);
		vst[G]=1;
		Calc(G);
		for(int p=e.h[G];p;p=e.next[p])if(!vst[e.to[p]]){
			sum=siz[e.to[p]];//注意改sum 
			stat(e.to[p]);
		}
	}
}p;
inline void init(){
	scanf("%d%d",&n,&k);
	Inc(i,1,n-1){
		int x,y,w;scanf("%d%d%d",&x,&y,&w);
		e.add(x+1,y+1,w),e.add(y+1,x+1,w);
	}
	p.ans=p.sum=n;
	memset(p.num,63,sizeof(p.num));
}
int main(){
	init();
	p.stat(1);
	if(p.ans==n)puts("-1");
	else cout<<p.ans<<'\n';
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dancingz/article/details/81120173