10月2日考试题解
$ T1 $ :
题意简述:
给一棵 $ n $ 个节点的树,点有非负权值,你需要找出两条点不相交的路径,使它们长度和最大。
$ n\le 10^5 $
$ solution: $
有直径的性质可知,最终两条路径只有两种情况:
- 两条路径其中一条为直径
- 两条路径都有一个端点为直径端点
于是我们两遍 $ DFS $ 找到一条直径,并记录这条直径。将这条直径标记(删掉),从直径的每个点开始向直径两边树型 $ DP $ ,找到从直径上每个点出发的最长链,以及不与直径相交的最长链(这个最长连和直径构成初始答案)。然后枚举直径上每一个点作为拐点(一条路径从这里离开直径),找到另一个最优的拐点,更新答案!
$ code: $
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 $ :
题意简述:
$ solution: $
$ code: $
$ T3 $ :
题意简述:
$ solution: $
$ code: $
原文地址:https://www.cnblogs.com/812-xiao-wen/p/11617670.html
时间: 2024-11-06 04:49:00