NOIP2015提高组Day2T3- 运输计划

传送门


Tips

如果两个点的LCA会被反复用到,就可以拿一个数组存下来,避免反复计算


Analysis

又是一道好喵喵喵妙的题啊!!!树上差分太棒了,简直太厉害

不过首先我们得看出来这是一道二分可以解决的问题
然后问题就变成了怎么check
看看gsj大佬怎么说吧


Code

注意常数因子带来的程序效率上的影响。
所以树链剖分貌似要快得多
但……hhh,写的倍增

#include<bits/stdc++.h>
#define in read()
#define N 600009
#define M 600009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9') {
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m,dis[N],dep[N],val[N];
int nxt[M],to[M],head[N],w[M],ecnt=0;
inline void add(int x,int y,int z){	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;}
int fa[N][25];
struct node{int u,v,lca,len;}p[N];
void dfs(int u,int fu){
	fa[u][0]=fu;dep[u]=dep[fu]+1;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fu) continue;
		dis[v]=dis[u]+w[e];val[v]=w[e];
		dfs(v,u);
	}
}
inline int getlca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=19;i>=0;--i) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if(x==y)	return x;	
	for(int i=19;i>=0;--i){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];y=fa[y][i];
		}
	}
	return fa[x][0];
}
int maxn,num,s[N],del;
void Dfs(int u){
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa[u][0]) continue;
		Dfs(v);
		s[u]+=s[v];
	}
	if(s[u]==num) del=max(del,val[u]);
}
inline bool check(int std){
	num=0;del=0;
	memset(s,0,sizeof(s));
	for(int i=1;i<=m;++i){
		if(p[i].len>std){
			num++;
			s[p[i].u]++;
			s[p[i].v]++;
			s[p[i].lca]-=2;
		}
	}
	Dfs(1);
	if(maxn-del<=std) return 1;
	return 0;
}
int main(){
	n=in;m=in;
	int i,j,k,a,b,c;
	for(i=1;i<n;++i){
		a=in;b=in;c=in;
		add(a,b,c);add(b,a,c);
	}
	dfs(1,0);
	for(j=1;j<=19;++j)
		for(i=1;i<=n;++i)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	for(i=1;i<=m;++i){
		p[i].u=in;p[i].v=in;
		p[i].lca=getlca(p[i].u,p[i].v);
		p[i].len=dis[p[i].u]+dis[p[i].v]-2*dis[p[i].lca];
		maxn=max(maxn,p[i].len);
	}
	int l=0,r=maxn,ans=0;//
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值