问题所在:提交到弱化版 P1099 能通过,n<=3000 以内与 std 对拍 3000+ 组数据全过。但交上去 0pts。
思路:找到树的直径后二分答案 x,然后两次遍历直径,检验能不能找到路径满足 x 限制且距离小于等于 s。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,s,dis[N],len[N],col[N],S,T,l,r;
int st[N],tp,f=1;
vector<pair<int,int> > e[N];
void dfs(int u,int fa){
if(dis[u]>dis[S]) S=u;
for(auto p:e[u]){
int v=p.first, w=p.second;
if(v==fa) continue;
dis[v]=dis[u]+w;
dfs(v,u);
}
}
void get(int u,int fa){
st[++tp]=u;
if(u==T){
f=0;
return;
}
for(auto p:e[u]){
int v=p.first;
if(v==fa) continue;
get(v,u);
if(f==0) return;
}
tp--;
}
void dfs2(int u,int fa){
for(auto p:e[u]){
int v=p.first, w=p.second;
if(v==fa) continue;
dfs2(v,u);
len[u]=len[v]+w;
}
}
bool chk(int x){
int x1=0,x2=0;
for(int i=1;i<=tp;i++){
for(auto p:e[st[i]]){
int v=p.first, w=p.second;
if(len[v]+w>x && col[v]==0) return false;
}
if(abs(dis[S]-dis[st[i]])>x){
if(i==1) x1=0;
else x1=abs(dis[S]-dis[st[i-1]]);
break;
}
}
for(int i=tp;i>=0;i--){
for(auto p:e[st[i]]){
int v=p.first, w=p.second;
if(len[v]+w>x && col[v]==0) return false;
}
if(abs(dis[T]-dis[st[i]])>x){
if(i==tp) x2=0;
else x2=abs(dis[T]-dis[st[i+1]]);
break;
}
}
if(dis[S]-x1-x2>s) return false;
return true;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>s;
for(int i=1;i<n;i++){
int x,y,z; cin>>x>>y>>z;
e[x].push_back({y,z});
e[y].push_back({x,z});
}
memset(dis,0,sizeof dis);
dfs(1,0);
memset(dis,0,sizeof dis);
T=S,S=0; dfs(T,0);
get(S,0);//找出直径路径
for(int i=1;i<=tp;i++){
col[st[i]]=1;//给直径上的点打上标记
}
dfs2(S,0);//以S为根求每个点到直径的距离最大值
l=0, r=dis[S];
while(l<r){//二分答案
int mid=(l+r)>>1;
if(chk(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}