洛谷 P2680 [NOIP2015 提高组] 运输计划(二分,树上查分,树上倍增,LCA)

传送门


比较综合的一道题。

解题思路

求把一条边变为0后这m条路径中的最短值,最大值最小,可以二分求解。

如何check某个答案x是否合法?
实质就是判断能否找到一条边,使得大于x的路径都经过这条边,并且减去这条边边权后路径长度都小于等于x。

于是我们先预处理出要求的路径的原长度(倍增求LCA),然后对于每个x,记录下每条边有多少原长超过x的路径经过(树上差分O(n)),再遍历一遍图,看看有没有满足条件的边(标记的经过路径数等于总共路径数量并且边权大于等于最长路径-x),做出判断。

注意事项:卡常。二分的右边界设置为最长路径。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=3e5+7;
int n,m,p[maxn],a[maxn],b[maxn],q[maxn],d[maxn],f[maxn],dp[maxn][21],dis[maxn],len[maxn],lca[maxn],l,r,maxlen,cnt;
struct node{
	int v,next,value;
}e[maxn*2]; 
void insert(int u,int v,int w){
	cnt++;
	e[cnt].v=v;
	e[cnt].value=w;
	e[cnt].next=p[u];
	p[u]=cnt;
}
void dfs(int u,int fa,int dep,int w){
	f[u]=fa;
	d[u]=dep;
	dis[u]=w;
	dp[u][0]=fa;
	for(int i=1;(1<<i)<dep;i++){
		dp[u][i]=dp[dp[u][i-1]][i-1];
	}
	for(int i=p[u];i!=-1;i=e[i].next){
		if(e[i].v==fa) continue;
		dfs(e[i].v,u,dep+1,w+e[i].value);
	}
}
int getlca(int x,int y){
	if(d[x]<d[y]) swap(x,y);
	int t=d[x]-d[y];
	for(int i=20;i>=0;i--){
		if(t&(1<<i)) x=dp[x][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--){
		if(dp[x][i]!=dp[y][i]){
			x=dp[x][i];
			y=dp[y][i];
		}
	}
	return dp[x][0];
}
void dfs2(int u){
	for(int i=p[u];i!=-1;i=e[i].next){
		if(e[i].v==f[u]) continue;
		dfs2(e[i].v);
		q[u]+=q[e[i].v];
	}
}
bool check(int x){
	memset(q,0,sizeof(q));
	int k=maxlen-x,cnt2=0;
	for(int i=1;i<=m;i++){
		if(len[i]>x){
			q[a[i]]++,q[b[i]]++,q[lca[i]]-=2;
			cnt2++;
		}
	}
	dfs2(1);
	for(int i=2;i<=n;i++){
		if(q[i]==cnt2&&dis[i]-dis[f[i]]>=k) return true;
	}
	return false;
}
int main(){
	memset(p,-1,sizeof(p));
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		insert(u,v,w);
		insert(v,u,w);
	}
	for(int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]);
	dfs(1,-1,1,0);
	for(int i=1;i<=m;i++){
		lca[i]=getlca(a[i],b[i]);
		len[i]=dis[a[i]]+dis[b[i]]-2*dis[lca[i]];
		r=max(r,len[i]);
	}
	l=0;
	maxlen=r;
	while(l<r){
		int mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	printf("%d",l);
    return 0;
}

//NOIP2015提高组Day2 t3

上一篇:[NOIp2015] 信息传递 题解


下一篇:NOIP2015普及组复赛----金币