题解:[NOIP 2015 提高组] 运输计划

题目传送门

题意分析

令最短时间为 \(mid\)

那么我们显然可以二分枚举 \(mid\),因为其具有单调性——\(mid\) 越大,则可供选择的虫洞方案就越多。

那么考虑如何较为高效地判断 \(mid\) 是否可行。

考虑到权值小于等于 \(mid\) 的计划不需要考虑,那么虫洞所在边一定是其余计划的公共边。

则可以用树上差分求出这些公共边是哪些,然后枚举删除即可。

判断删去之后能否成功,只需要判断权值最大的计划的权值减去该边的权值是否小于 \(mid\) 即可。

时间复杂度:\(\mathcal O\left(n\log n+(n+m)\log V\right)\)\(V\) 为答案值域大小。

卡常

求路径长度需要 lca,使用“DFS 序+ST 表”即可。

每次清空树上差分数组时,可使用时间戳优化。

快读。

\(w\) 为所有计划的最大权值,\(t\) 为最长边的权值,则答案是在 \([w-t,w]\) 中的。

因此答案值域 \(V=t\)\(V\) 有上界 \(1000\)

卡常之后时间复杂度为 \(\mathcal O(n\log n)\)

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
#define getchar getchar_unlocked
#define putchar putchar_unlocked
constexpr const int N=3e5,M=3e5,T=1000;
vector<pair<int,int> >g[N+1];
int n,m;
struct Plan{
	int u,v,w;
}plan[M+1];
int st[N+1][__lg(N)+1],rest[N+1][__lg(N)+1],father[N+1],depth[N+1],order[N+1],first[N+1],sum[N+1];
void dfs(int x,int fx){
	static int cnt;
	order[++cnt]=x;
	first[x]=cnt;
	father[x]=fx;
	depth[x]=depth[fx]+1;
	for(auto i:g[x]){
		if(i.first==fx){
			continue;
		}
		sum[i.first]=sum[x]+i.second;
		dfs(i.first,x);
	}
}
void lca_pre(){
	dfs(1,0);
	for(int i=1;i<=n;i++){
		st[i][0]=depth[order[i]];
		rest[i][0]=order[i];
	}
	for(int i=1;(1<<i)<=n;i++){
		for(int x=1;x+(1<<i)-1<=n;x++){
			int &plA=st[x][i-1],&plB=st[x+(1<<i-1)][i-1];
			if(plA<plB){
				st[x][i]=plA;
				rest[x][i]=rest[x][i-1];
			}else{
				st[x][i]=plB;
				rest[x][i]=rest[x+(1<<i-1)][i-1];
			}
		}
	}
}
int lca(int u,int v){
	if(u==v){
		return u;
	}
	if(first[u]>first[v]){
		swap(u,v);
	}
	int s=__lg(first[v]-first[u]);
	if(st[first[u]+1][s]<st[first[v]-(1<<s)+1][s]){
		return father[rest[first[u]+1][s]];
	}else{
		return father[rest[first[v]-(1<<s)+1][s]];
	}
}
int diff[N+1],tag[N+1],Time;
void dfs2(int x,int fx){
	if(tag[x]!=Time){
		tag[x]=Time;
		diff[x]=0;
	}
	for(auto i:g[x]){
		if(i.first==fx){
			continue;
		}
		dfs2(i.first,x);
		diff[x]+=diff[i.first];
	}
}
bool check(int mid){
	Time++;
	int all=0;
	int Max=-1;
	for(int i=1;i<=m;i++){
		if(plan[i].w>mid){
			int &u=plan[i].u,&v=plan[i].v;
			int p=lca(u,v);
			if(tag[p]!=Time){
				tag[p]=Time;
				diff[p]=0;
			}
			diff[p]-=2;
			if(tag[u]!=Time){
				tag[u]=Time;
				diff[u]=0;
			}
			if(tag[v]!=Time){
				tag[v]=Time;
				diff[v]=0;
			}
			diff[u]++,diff[v]++;
			all++;
			Max=max(Max,plan[i].w);
		}
	}
	dfs2(1,0);
	for(int i=1;i<=n;i++){
		if(diff[i]==all){
			if(Max-(sum[i]-sum[father[i]])<=mid){
				return true;
			}
		}
	}
	return false;
}
template<typename T> 
inline void Read(T &x){
	x=0;
	register int f=1;
	register char ch=getchar();
	for(;ch<'0'||'9'<ch;ch=getchar())if(ch=='-')f=-1;
	for(;'0'<=ch&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
	x*=f;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
//	ios::sync_with_stdio(false);
//	cin.tie(0);cout.tie(0);
	
	Read(n);Read(m);
//	cin>>n>>m;
	int maxT=0;
	for(int i=1;i<n;i++){
		int a,b,t;
		Read(a);Read(b);Read(t);
//		cin>>a>>b>>t;
		g[a].push_back({b,t});
		g[b].push_back({a,t});
		maxT=max(maxT,t);
	}
	lca_pre();
	for(int i=1;i<=m;i++){
		int &u=plan[i].u,&v=plan[i].v;
		Read(u);Read(v);
//		cin>>u>>v;
		int p=lca(u,v);
		plan[i].w=sum[u]+sum[v]-(sum[p]<<1);
	}
	int maxW=0;
	for(int i=1;i<=m;i++){
		maxW=max(maxW,plan[i].w);
	}
	int l=maxW-maxT,r=maxW+1;
	while(l<r){
		int mid=l+r>>1;
		if(check(mid)){
			r=mid;
		}else{
			l=mid+1;
		}
	}
	cout<<r<<'\n';
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-21 22:04  TH911  阅读(11)  评论(0)    收藏  举报