基环树求直径

概念

树结构是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;
}
posted @ 2025-09-24 17:35  xdhking  阅读(20)  评论(0)    收藏  举报