문제가 10 월 2 일 테스트 솔루션
$ T1 $ :
질문에 대한 설명의 의미 :
트리의 $ n 개의 $ 노드, 요점은 음이 아닌 가중치를 가지고, 당신은 경로가 교차하지 않는 두 점을 찾을 필요가 그래서 그들은 최대 길이.
N $ \ 10 ^ 5 $
$ 솔루션 : $
명백한 직경의 성격, 마지막 두 경로 다음 두 가지 경우에만 :
- 상기 두 경로의 직경
- 두 경로는 직경 엔드 포인트를
그래서 우리는 두 배의 직경이 레코드의 직경 달러 (A $) DFS $를 찾을 수 있습니다. (이 직경 마커 (삭제), 민주당 $ $ 나무의 반경 측면의 시작부터 각 지점의 직경, 직경의 각 지점에서 시작하여 가장 긴 체인을 찾을 수와 교차하지 않는 긴 체인의 직경을 넣어 심지어 가장 긴 직경과 초기 구성 응답). 그런 전환점으로 각 지점을 열거 (여기 직경에서 길을 떠나), 또 다른 최적의 전환점을 찾을 응답 직경을 업데이트!
$ 코드 : $
ll ans,tot;
int n,tt,rt,id,mx;
ll a[100005];
ll d[100005];
ll s[100005];
ll v[100005];
int t[100005];
bool vis[100005];
struct su{
int to,next;
}b[200005];
int tou[100005],top;
inline void dfs(int i){ //找直径
vis[i]=1; if(d[i]>d[mx]) mx=i;
for(rg j=tou[i];j;j=b[j].next){
rg to=b[j].to; if(vis[to])continue;
d[to]=d[i]+a[to]; dfs(to);
} vis[i]=0;
}
inline bool find(int i,int x){ //开栈记录最长链
vis[i]=1; t[++tt]=i; if(i==x) return 1;
for(rg j=tou[i];j;j=b[j].next){
rg to=b[j].to; if(vis[to])continue;
d[to]=d[i]+a[to]; if(find(to,x))return 1;
} vis[i]=0; --tt; return 0; //vis数组会保留对直径的标记
}
inline void dp(int i){ //找到从每个点出发的最长链
vis[i]=1; d[i]=0;
for(rg j=tou[i];j;j=b[j].next){
rg to=b[j].to; if(vis[to])continue;
dp(to); tot=max(tot,d[to]+d[i]+a[i]); //tot记录不与直径交的最长链
d[i]=max(d[i],d[to]);
} vis[i]=0; d[i]+=a[i]; tot=max(tot,d[i]);
}
int main(){
n=qr();
for(rg i=1;i<=n;++i) a[i]=qr();
for(rg i=1;i<n;++i){
rg x=qr(),y=qr();
b[++top]=su{y,tou[x]}; tou[x]=top;
b[++top]=su{x,tou[y]}; tou[y]=top;
} d[1]=a[1]; dfs(1); rt=mx; mx=0; //找直径端点
d[rt]=a[rt]; dfs(rt); id=mx; mx=0; //找直径
find(rt,id); //标记直径
for(rg i=1;i<=tt;++i){
rg x=t[i]; s[i]=d[x];
for(rg j=tou[x];j;j=b[j].next){ //从直径每个点出发
rg to=b[j].to; if(vis[to])continue;
dp(to); v[i]=max(v[i],d[to]); //树型dp
}
}ans=tot+s[tt]; tot=0;
for(rg i=1;i<=tt;++i){ //计算最终答案
ans=max(ans,tot-s[i-1]+v[i]+s[tt]);
tot=max(tot,s[i]+v[i]);
}
printf("%lld\n",ans);
return 0;
}
$ $ T2 :
질문에 대한 설명의 의미 :
$ 솔루션 : $
$ 코드 : $
$ T3 $ :
질문에 대한 설명의 의미 :
$ 솔루션 : $
$ 코드 : $