P1024 万山舆共渡 官方题解
Gioush OJ · P1024 万山舆共渡
万山舆共渡
【题目简述】
给定一棵带权树和 条运输路径。可以选择一条边使其边权变为 ,求所有路径长度最大值的最小可能值。
【数据点 1∼81\sim81∼8】
【一条运输计划】
当 时,直接找出这条路径,设长度为 、其中最大边权为 。显然清零最大边最优,答案为 。当 均不大时,枚举清零哪条边,再重新计算所有路径并取最大值。
【数据点 9∼149\sim149∼14】
【链上的判定】
二分答案 。原长度不超过 的运输计划无需处理;其余所有超时路径都必须经过同一条被清零的道路。
【解法】
对所有超时区间做差分。设超时路径有 条,若某条道路被覆盖了 次,它便同时属于全部超时路径。所有这种道路中取权值最大的 ;设原最大路径长为 ,当且仅当 时当前 合法。
【正解】
【树上差分】
对每条超时路径 ,令 ,作
【解法】
回溯时将儿子的差分和加到父亲。把道路 看作结点 派生的道路,则回溯后的 正是这条道路被多少条超时路径覆盖。
若存在道路满足 且边权 ,清零它后所有超时路径都不超过 ;反之,任何可行道路都必须同时满足这两个条件。
【复杂度分析】
LCA 预处理为 ;一次判定为 ,总复杂度为 。
【参考代码】
#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 300010using namespace std;int Total=0,Head[MAXN],Depth[MAXN],Count[MAXN],Fa[MAXN][22],D[MAXN],Diff[MAXN];struct edge{ int Ed,Next,Val;}Edge[MAXN<<1];void Edge_Add(int St,int Ed,int Val){ Edge[++Total]={Ed,Head[St],Val}; Head[St]=Total;}struct Plan{ int u,v,Val,lca;}p[MAXN];bool cmp(Plan a,Plan b) {return a.Val<b.Val;}void DFS(int Now,int From){ for(int i=1;i<=21;i++){ Fa[Now][i]=Fa[Fa[Now][i-1]][i-1]; } for(int i=Head[Now];i;i=Edge[i].Next){ int To=Edge[i].Ed; if(To==From) continue; D[To]=D[Now]+1; Depth[To]=Depth[Now]+Edge[i].Val; Fa[To][0]=Now; DFS(To,Now); } return;}inline int LCA(int x,int y){ if(D[x]<D[y]) swap(x,y); for(int i=21;i>=0;i--){ if(D[Fa[x][i]]>=D[y]) x=Fa[x][i]; } if(x==y) return x; for(int i=21;i>=0;i--){ if(Fa[x][i]!=Fa[y][i]){ x=Fa[x][i];y=Fa[y][i]; } } return Fa[x][0];}int n,m,In1,In2,In3;bool Tag=0;inline int Run(int Now,int From,int cnt,int t){ int Sum=0,tmp=0; for(int i=Head[Now];i;i=Edge[i].Next){ int To=Edge[i].Ed; if(To==From)continue; tmp=Run(To,Now,cnt,t); Sum+=tmp; if(tmp==cnt&&Edge[i].Val>=t){Tag=1;} } return Sum+Diff[Now];}bool Judge(int t){ int cnt=0; Tag=0; memset(Diff,0,sizeof(Diff)); for(int i=1;i<=m;i++){ if(p[i].Val<=t) continue; Diff[p[i].u]++;Diff[p[i].v]++; Diff[p[i].lca]-=2; cnt++; } Run(1,1,cnt,p[m].Val-t); if(Tag) return 1; else return 0;}int main(){ memset(Depth,0,sizeof(Depth)); memset(Head,0,sizeof(Head)); memset(D,0,sizeof(D)); scanf("%d%d",&n,&m); for(int i=1;i<=n-1;i++){ scanf("%d%d%d",&In1,&In2,&In3); Edge_Add(In1,In2,In3); Edge_Add(In2,In1,In3); } D[1]=1; DFS(1,1); for(int i=1;i<=m;i++){ scanf("%d%d",&p[i].u,&p[i].v); p[i].lca=LCA(p[i].u,p[i].v); p[i].Val=Depth[p[i].u]+Depth[p[i].v]-2*Depth[p[i].lca]; } sort(p+1,p+m+1,cmp); int l=0,r=p[m].Val; while(l<r){ int Mid=(l+r)>>1; if(Judge(Mid)) r=Mid; else l=Mid+1; } printf("%d",l); return 0;}