题目描述 传送门
L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。
小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物
流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。
为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后, 这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的 物流公司的阶段性工作就完成了。
如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段时要的最短时间是多少?
树链剖分+线段树暴力尝试修改所有路径40分,,,
正解:二分答案+树上差分+LCA(我用的树链剖分)
大致思路是:先把无根树转化成有根树,预处理lca和运输的路径长,然后二分答案,对于大于二分答案的路径,需要尝试删除其中一条边来满足答案,易知需要删除的边一定是这些路径的交集,利用树上差分求出每条边被多少条大于答案的路径覆盖,方法是把一条
(u,v)
路径分成两条
(u,lca(u,v)),(v,lca(u,v))
的链,类似差分数列,给最前面的+1,超尾位置-1,前缀和就是实际值,那么就是sum[u]–,sum[v]–,sum[lca(u,v)]-=2;最后根据dfs序递推出前缀和,贪心尝试删除最大的那条交边,继续二分直到不能分。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int n,m;
struct Edge{
int to,nxt,d;
Edge(int a=0,int b=0,int c=0):to(a),nxt(b),d(c){}
}v[maxn*2];
int len=0,cnt=0,l=-1,r=0;
int h[maxn],fa[maxn],sz[maxn],son[maxn],lca[maxn],seq[maxn],dist[maxn],cost[maxn],depth[maxn],top[maxn],s[maxn],t[maxn],sum[maxn];
void dfs1(int u,int f){
fa[u]=f;
sz[u]=1;
son[u]=0;
depth[u]=depth[f]+1;
for(int i=h[u];i;i=v[i].nxt) if(v[i].to!=f){
dist[v[i].to]=dist[u]+v[i].d;
dfs1(v[i].to,u);
sz[u]+=sz[v[i].to];
if(sz[v[i].to]>sz[son[u]]) son[u]=v[i].to;
}
seq[len++]=u;
}
void dfs2(int u,int tp){
top[u]=tp;
if(sz[u]>1) dfs2(son[u],tp);
for(int i=h[u];i;i=v[i].nxt) if(v[i].to!=fa[u]&&v[i].to!=son[u])
dfs2(v[i].to,v[i].to);
}
int getlca(int x,int y){
int f1=top[x],f2=top[y],ans=0;
while(f1!=f2){
if(depth[f1]<depth[f2]) swap(f1,f2),swap(x,y);
x=fa[f1];
f1=top[x];
}
if(x==y) return x;
if(depth[x]<depth[y]) swap(x,y);
return y;
}
bool check(int a){
memset(sum,0,sizeof(sum));
int tot=0,ans=0;
for(int i=0;i<m;i++) if(cost[i]>a){
sum[lca[i]]-=2;
sum[s[i]]++;
sum[t[i]]++;
tot++;
}
for(int i=0;i<len;i++) sum[fa[seq[i]]]+=sum[seq[i]];
for(int i=2;i<=n;i++) if(sum[i]==tot) ans=max(ans,dist[i]-dist[fa[i]]);
for(int i=0;i<m;i++) if(cost[i]-ans>a) return false;
return true;
}
int main(){
scanf("%d%d",&n,&m);
memset(h,0,sizeof(h));
for(int i=1;i<n;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
v[++cnt]=Edge(b,h[a],w);
h[a]=cnt;
v[++cnt]=Edge(a,h[b],w);
h[b]=cnt;
r+=w;
}
depth[1]=sz[0]=dist[1]=0;
dfs1(1,1);
dfs2(1,1);
for(int i=0;i<m;i++){
scanf("%d%d",&s[i],&t[i]);
lca[i]=getlca(s[i],t[i]);
cost[i]=dist[s[i]]+dist[t[i]]-2*dist[lca[i]];
}
while(l<r-1){
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
printf("%d\n",r);
return 0;
}