我是超链接
这个题目的关键:我们选择的路径一定在直径上。可以用反证法
如果整条路径与直径没有交集,那么可以从其中一点走到直径上,然后走到直径一端,发现那个点到直径一端的距离一定大于从直径上一点直接到ta的距离,因为直径上另一边的一端没有选择路径那一部分作为直径的另一半
如果有交集,此路径与直径会在一个点岔开,对于岔开的点一定需要通过ta到达直径的某一端,而如果选择直径的话就是到达该路径的一端,我们的该路径怎么会比直径长呢?
所以路径一定在树的直径上!
我们可以dfs求出树的直径,然后(熟练地)记录下这条链上的点,求出这些点到离ta最远的叶子结点(不经过直径上的点)的值
整个直径的长度很有可能超过s,所以我们要考虑截取直径的一段。
看到这种最大值最小的问题,首先应该会想到二分,我们二分最大值,然后考虑从直径的两端向内缩,直到不能缩为止,判断此时的长度与s的关系看是否可行。
那么什么情况就不能缩了呢,就是连在这个点的最远的叶子节点的长度(包括已经从直径中t出去的点)>二分的答案的时候就不能再缩了。
注意len记录的是到达gen的距离,所以在上面的距离小,下面的距离大,应该用距离大的-距离小的
#include
#include
#include
#define N 300005
using namespace std;
int tot,nxt[N*2],point[N],v[N*2],c[N*2],len[N],maxx,gen,father[N],cha[N],maxn[N],dis[N],sy[N],s;
bool vis[N];
void addline(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs(int x,int fa)
{
if (len[x]>maxx) maxx=len[x],gen=x;
father[x]=fa;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
len[v[i]]=len[x]+c[i];
dfs(v[i],x);
}
}
void dfs2(int x,int fa)
{
vis[x]=1;
maxx=max(maxx,dis[x]);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa && !vis[v[i]])
{
dis[v[i]]=dis[x]+c[i];
dfs2(v[i],x);
}
}
void Chain(int x)
{
while (x)
{
vis[x]=1;
cha[++cha[0]]=x;
x=father[x];
}
}
bool check(int mid)
{
int l=1,r=cha[0],i;
for (i=1;i<=cha[0];i++)
{
sy[cha[i]]=maxn[cha[i]];
if (sy[cha[i]]>mid) return 0;
}
while (1)
{
sy[cha[l+1]]=max(sy[cha[l+1]],sy[cha[l]]+len[cha[l+1]]-len[cha[l]]);
if (sy[cha[l+1]]>mid || l+1>r) break;
l++;
}
for (i=1;i<=cha[0];i++) sy[cha[i]]=maxn[cha[i]];
while (1)
{
sy[cha[r-1]]=max(sy[cha[r-1]],sy[cha[r]]+len[cha[r]]-len[cha[r-1]]);
if (sy[cha[r-1]]>mid || l+1>r) break;
r--;
}
return (len[r]-len[l])<=s;//链长短了,可以缩的太多了,你这个mid值太大了啊
}
int main()
{
int n,i,l=0,r=0;
scanf("%d%d",&n,&s);
for (i=1;iint x,y,z;
scanf("%d%d%d",&x,&y,&z);
addline(x,y,z);r+=z;
}
dfs(1,0);memset(len,0,sizeof(len));maxx=0;dfs(gen,0);
Chain(gen);
for (i=1;i<=cha[0];i++)
{
maxx=0;
dfs2(cha[i],0);
maxn[cha[i]]=maxx;
}
memset(len,0,sizeof(len));maxx=0;dfs(gen,0);
int ans=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
}