蒟 蒻 求 教 啊 啊 啊 啊 啊 啊 啊 带 注 释
查看原帖
蒟 蒻 求 教 啊 啊 啊 啊 啊 啊 啊 带 注 释
183609
hhoppitreeMadeline楼主2020/5/19 17:06
#include<bits/stdc++.h> 
using namespace std;
inline int read(){
    int res=0;
    char c;
    bool zf=0;
    while(((c=getchar())<'0'||c>'9')&&c!= '-');
    if(c=='-')zf=1;
    else res=c-'0';
    while((c=getchar())>='0'&&c<='9')res=(res<<3)+(res<<1)+c-'0';
    if(zf)return -res;
    return res;
}
int n;
int ask;
int Next[400005],go[400005],pre[400005],len[400005],r;
int rt;
int SIZE[200005],MAXSIZE[200005];
bool used[200005];
int sum;
inline void insert(int u,int v,int c){
	r++;
	Next[r]=pre[u];
	go[r]=v;
	len[r]=c;
	pre[u]=r;
	return;
}
inline void add(int u,int v,int c){
	insert(u,v,c);
	insert(v,u,c);
	return;
}
void getrt(int now,int father){//now表示当前节点,father表示父亲 
	SIZE[now]=1;//当前节点 
	MAXSIZE[now]=0;//没有子树 
	for(register int i=pre[now];i;i=Next[i]){//遍历每条边 
		int v=go[i];//求儿子的节点 
		if(used[v]||v==father)continue;//used[v]不能为true且不能递归父亲 
		getrt(v,now);//递归儿子 
		SIZE[now]+=SIZE[v];//当前节点及子树大小加上儿子及其子树的大小 
		MAXSIZE[now]=max(MAXSIZE[now],SIZE[v]);//更新最大子树大小 
	}
	MAXSIZE[now]=max(MAXSIZE[now],sum-SIZE[now]);//和sum-SIZE[now]取max 
	if(MAXSIZE[now]<MAXSIZE[rt])rt=now;//更新rt 
	return;
}
int ans=200005;
int judge[100005];
int cnt,tmp[200005],llen[200005],lllen[200005],dis[200005];
void getdis(int now,int fa){//当前节点和父亲 
	if(dis[now]>1e6)return;//如果保证边权非负,可以加这句话 
	//距离太长不统计,因为题目没有问那么大,求了有何用?
	//甚至会导致RE,因为数组会越界(在其它函数里)
	//但是如果不保证边权非负,就不能加这句话,否则会WA
	//所以如果不保证边权非负,就要开大数组
	//不然,RE欢迎您... 
	tmp[++cnt]=dis[now];//存入临时数组 
	llen[cnt]=lllen[now];
	for(register int i=pre[now];i;i=Next[i]){//遍历每条边 
		int v=go[i];//求儿子的节点 
		if(used[v]||v==fa)continue;
		//used[v]不能为true,并且不能再递归父亲 
		dis[v]=dis[now]+len[i];//统计儿子到当前根节点的长度 
		lllen[v]=lllen[now]+1;
		getdis(v,now);//递归求解儿子 
	}
	return;
}
inline void work(int now){//now表示当前节点 
	queue<int> Q;//详见20~23行 
	for(register int i=pre[now];i;i=Next[i]){//遍历每条边 
		int v=go[i];//求儿子的节点 
		if(used[v])continue;//used[v]不能为true
		cnt=0;//模拟的vector的长度 
		lllen[v]=1;dis[v]=len[i];//到这颗子树的根节点的距离 
		getdis(v,now);//递归求整棵子树的距离 
		sort(tmp+1,tmp+cnt+1);
		for(register int i=1;i<=cnt;i++){
			if(ask>=tmp[i]&&judge[ask-tmp[i]]!=-1){
				ans=min(ans,llen[i]+judge[ask-tmp[i]]);
			}
		}
		for(register int i=1;i<=cnt;i++){//遍历子树的每个节点
			Q.push(tmp[i]);//详见92~95行 
			judge[tmp[i]]=(judge[tmp[i]]==-1?llen[i]:min(judge[tmp[i]],llen[i]));//把judge[tmp[i]]设置为true,表示存在节点使那个节点到当前节点的距离为tmp[i] 
		}
	}
	while(Q.size()){ 
		judge[Q.front()]=-1;
		Q.pop();
	}
	/*
		这么写的原因是用memset会TLE,所以要用一个队列来存。
		(stack,vector也行,甚至再建一个数组也行) 
	*/ 
	return;
}
void solve(int now){//now表示当前节点 
	judge[0]=0;used[now]=1;work(now);//初始化及统计 
	for(register int i=pre[now];i;i=Next[i]){//遍历每条边 
		int v=go[i];//求儿子的节点 
		if(used[v])continue;//used[v]不能为true(used之后讲是什么数组) 
		sum=SIZE[v];MAXSIZE[rt=0]=sum;//更新sum和初始化rt
		getrt(v,0);getrt(rt,0);//要getrt两遍
		solve(rt);//递归求解子树的重心 
	}
	return;
}
int main(){
	memset(judge,-1,sizeof(judge));
	n=read();ask=read();sum=n;//n表示节点个数,m表示询问个数,初始化sum
	for(register int i=1;i<n;i++){//读入树 
		int u=read()+1,v=read()+1,c=read();//两个端点及长度 
		add(u,v,c);//链式前向星存图 
	} 
	MAXSIZE[rt=0]=n;//初始化rt 
	getrt(1,0);getrt(rt,0);//要getrt两遍
	solve(rt);//求解 
	cout<<(ans>200000?-1:ans)<<'\n';
	return 0;
}
2020/5/19 17:06
加载中...