洛谷 P4244 [SHOI2008]仙人掌圖 II

題目鏈接: https://www.luogu.org/problem/P4244

題意:

無向圖仙人掌求直徑,即這張圖相距最遠的兩個點的距離,距離爲兩個點之間的最短路長度。

做法:

必要的過程解釋都已經寫在代碼裏了。

簡單來說,如果是一棵樹,那麼直接用 d[x]d[x] 來表示點 xx 往下的兒子到 xx 的最長距離,每次用最長和次長距離更新答案即可。

但是因爲仙人掌圖是存在環的,所以把環上的情況全部存在第一次發現環的這個根節點上。如何存呢,大概的意思就是先將環上的點一次保存,用尺取的方法不斷地去用環上最遠的兩個點之間的距離(加上這些點向外延伸的距離)去更新答案。 最後再把通過這個環可以到達的最遠距離更新在根節點上,這個根節點可以再去更新其他值。

代碼

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define rep_e(i,u,v) for(int i=head[u],v=to[i];~i;i=nex[i],v=to[i])
using namespace std;
typedef long long ll;
const int maxn=50005;
const int maxm=2000005;
int n,m,low[maxn],dfn[maxn],d[maxn];
int head[maxn],to[maxm],nex[maxm],cnt,ans;
int a[maxn*2],st[maxn*2],ct,dep[maxn],fa[maxn];
void add(int u,int v){
    to[cnt]=v;nex[cnt]=head[u];
    head[u]=cnt++;
}
void deal(int rt,int x){
    // 用深度差確定這個環上有多少邊
    int tot=dep[x]-dep[rt]+1,tmp=tot;

    //將這個環上的點向外的最遠距離記錄
    for(int i=x;i!=rt;i=fa[i]){
        a[tmp--]=d[i];
    }
    a[tmp]=d[rt];

    //將點翻倍 便於做環的處理(環中是用的尺取的方法)
    rep(i,1,tot) a[tot+i]=a[i];
    st[1]=1;
    int l=1,r=1;
    rep(i,2,2*tot){

        //如果左端和右端的距離超過一半的環 那麼肯定已經不合法
        while(l<=r&&i-st[l]>tot/2) l++;

        //st爲遞減的單調棧 l存的是最大的a[x]-x
        //表示從i出去的最長邊far[i]+far[st[l]]+點st[l]和i在換上的距離
        ans=max(ans,a[i]+i+a[st[l]]-st[l]);

        //維護一個a[i]-i遞減的單調棧
        while(l<=r&&a[st[r]]-st[r]<=a[i]-i) r--;
        st[++r]=i;
    }

    //用根節點來存從環延伸出去的最長的邊
    rep(i,2,tot) d[rt]=max(d[rt],a[i]+min(i-1,tot-(i-1)));
}
void dfs(int u,int f){
    low[u]=dfn[u]=++ct;
    rep_e(i,u,v){
        if(v==f) continue;
        if(!dfn[v]){
            fa[v]=u; dep[v]=dep[u]+1;   //更新點的父親以及深度
            dfs(v,u); low[u]=min(low[v],low[u]);
        }
        else low[u]=min(low[u],dfn[v]);
        if(dfn[u]<low[v]){              //該邊爲非環邊 當做樹邊來做
            ans=max(ans,d[u]+d[v]+1);
            d[u]=max(d[u],d[v]+1);
        }
    }
    rep_e(i,u,v)
        if(fa[v]!=u&&dfn[u]<dfn[v])//找到了環
            deal(u,v);

}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    rep(o,1,m){
        int k,x,y; scanf("%d",&k);
        scanf("%d",&x);
        rep(i,2,k){
            scanf("%d",&y);
            add(x,y); add(y,x); x=y;
        }
    }
    dep[1]=1;
    dfs(1,-1);
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章