题目链接 https://www.luogu.org/problemnew/show/P2680
一.审题-关键点:
1.只能建造一个虫洞,也就是只能将一条边的权值改成0
2.求的实质上是最长路最短
二.思路……
1.所有的都是一棵树上的两点的距离->自然想到lca求两点距离
2.然后求完了距离要求最值。因为是最长路最短,很自然想到二分答案
3.二分固定最长路最短,也就是说所有的路径都要小于这个值x。(下面是check函数)
4.找出所有长度>x的路径,存下来,开始去边。
5.我们去掉的边一定满足两个条件:
1)所有>x的路径都经过这条边(不然肯定超啊。。。)
2)超的最多的那条边减掉这条边的长度<=x
6.由条件1就能想到把所有路径经过的道路标一标,经过次数==超过x的路径数的存下来。这里就能想到树状差分求一下就行啦。
7.最后肯定是这些存下来的边中的权值最大的最优啊(这不是废话嘛。。。)找到权值最大的,判断一下条件2就做完咯。
代码在这里(敲黑板)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=300005;
vector<int> more;
int n,m,cnt;
int v[maxn*2],w[maxn*2],nxt[maxn*2],head[maxn],len[maxn];
int d[maxn],dep[maxn],p[maxn][33],dis[maxn],anc[maxn];
int c[maxn];
struct node
{
int x,y;
}a[maxn];
int cmp(int x,int y)
{
return dis[x]>dis[y];
}
void add_edge(int x,int y,int z)
{
cnt++;v[cnt]=y;w[cnt]=z;
nxt[cnt]=head[x];head[x]=cnt;
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
int i;for(i=0;(1<<i)<=d[x];i++);i--;
for(int j=i;j>=0;j--) if(d[x]-(1<<j)>=d[y]) x=p[x][j];
if(x==y) return x;
for(int j=i;j>=0;j--)
{
if(p[x][j]!=p[y][j]) {x=p[x][j];y=p[y][j];}
}
return p[x][0];
}
void find(int x,int fa)
{
d[x]=d[fa]+1;p[x][0]=fa;
for(int i=head[x];i!=-1;i=nxt[i])
{
if(v[i]!=fa)
{
dep[v[i]]=dep[x]+w[i];
len[v[i]]=w[i];
find(v[i],x);
}
}
}
void prework()
{
for(int j=1;j<=32;j++)
{
for(int i=1;i<=n;i++) p[i][j]=p[p[i][j-1]][j-1];
}
}
void dfs(int x)
{
for(int i=head[x];i!=-1;i=nxt[i])
{
if(v[i]!=p[x][0])
{
dfs(v[i]);
c[x]+=c[v[i]];
}
}
}
bool ok(int x)
{
more.clear();
for(int i=1;i<=m;i++) {if(dis[i]>x) more.push_back(i);}
if(more.empty()) return 1;
memset(c,0,sizeof(c));
sort(more.begin(),more.end(),cmp);
for(int i=0;i<more.size();i++)
{
int tmp=lca(a[more[i]].x,a[more[i]].y);
c[a[more[i]].x]++;c[a[more[i]].y]++;c[tmp]-=2;
}
dfs(1);
int maxv=0;
for(int i=1;i<=n;i++)
{
if(c[i]>=more.size())
{
maxv=max(maxv,len[i]);
}
}
return dis[more[0]]-maxv<=x;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);add_edge(y,x,z);
}
find(1,0);prework();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
anc[i]=lca(a[i].x,a[i].y);
dis[i]=dep[a[i].x]+dep[a[i].y]-2*dep[anc[i]];
}
int l=0,r=1e9,ans=0;
while(l<=r)
{
int mid=(l+r)/2;
if(ok(mid))
{
ans=mid;r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}