基环树求直径
概念
树结构是n个顶点,n-1条边,如果多加一条边会变成一个图结构,此时这个图结构中只有一个环,这样的图称为基环树,即n 个点 n 条边的无重边无自环的连通无向图
code,模板洛谷P12145:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int dp[N],t[N],in[N],n,ret;
vector<int> edges[N];
int a[N<<1];//环的各结点编号,按顺序储存
int pre[N<<1];//环的权值前缀和
int sz=1;//环的大小
bool st[N];
void topu(queue<int>& q){
while(q.size()){
int u=q.front();
q.pop();
for(int &v:edges[u]){
//入度为一时,已经作为叶子结点入队了,直接忽略
if(in[v]==1)
continue;
in[v]--;
//无向图,入度为一的为叶子
if(in[v]==1){
q.push(v);
}
}
}
}
void find_loop(int u){
for(int &v:edges[u]){
if(in[v]==1||st[v])
continue;
a[++sz]=v;
st[v]=true;
find_loop(v);
}
}
int dfs(int u,int fa){
//以u为根的,叶子到根的权值最大路径与次大路径之和
//不包括根的权值
int tmp=0;
for(int &v:edges[u]){
if(v==fa||in[v]==2)
continue;
dfs(v,u);
//情况一:子树存在路径获得最大权值,与环无关的答案
ret=max(ret,dp[u]+dp[v]+t[u]);
tmp=max(tmp,dp[u]+dp[v]);
dp[u]=max(dp[u],dp[v]);
}
dp[u]+=t[u];
return tmp;
}
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
cin>>t[i];
}
for(int i=1;i<=n;i++){
int u,v;
cin>>u>>v;
edges[u].push_back(v);
edges[v].push_back(u);
in[u]++;
in[v]++;
}
queue<int> q;
for(int i=1;i<=n;i++){
if(in[i]==1){
q.push(i);
}
}
//拓扑排序找环
topu(q);
//找环的起始位置
for(int i=1;i<=n;i++){
if(in[i]>=2){
a[1]=i;
break;
}
}
//打表a数组
st[a[1]]=true;
find_loop(a[1]);
//复写
for(int i=1;i<=sz;i++){
a[i+sz]=a[i];
}
for(int i=1;i<=sz+sz;i++){
pre[i]=pre[i-1]+t[a[i]];
}
for(int i=1;i<=sz;i++){
//情况二:子树与整个环获得最大权值
ret=max(ret,dfs(a[i],0)+pre[sz]);
dp[a[i]]-=t[a[i]];
}
//单调队列维护窗口最大值
deque<int> mx;
for(int r=1;r<=sz+sz;r++){
while(mx.size()&&r-mx.front()+1>sz){
mx.pop_front();
}
if(mx.size()){
//情况三:两条子树的权值最大权值路径加上部分环的权值
ret=max(ret,dp[a[r]]+dp[a[mx.front()]]+pre[r]-pre[mx.front()-1]);
}
while(mx.size()>=2&&dp[a[mx.back()]]-pre[mx.back()-1]<=dp[a[r]]-pre[r-1]){
mx.pop_back();
}
mx.push_back(r);
}
cout<<ret<<endl;
return 0;
}

浙公网安备 33010602011771号