1. 程式人生 > 實用技巧 >[BZOJ4331] [JSOI2012]越獄老虎橋

[BZOJ4331] [JSOI2012]越獄老虎橋

[BZOJ4331] [JSOI2012]越獄老虎橋

首先根據題目的意思,這個無向圖中 每一個邊雙聯通分量 的點都是等價的

據此,建出一棵邊雙生成樹,樹邊即為原圖的割邊,樹邊帶權

考慮最優的情況實際上是連線了兩個葉子\((u,v)\),把圖展開後,就會發現,就是把\(u,v\)到根路徑上所有的點都縮進了同一個環

由於這個環是與1相連的,所以斷掉環上的邊顯然不合法,而不在環上的邊,只需要隨便斷掉一條,就能讓一個點不連通

也就是說,答案是 (去掉兩條\((1,u)\)鏈上的所有邊,剩下的邊中最小值) 的最大值

設答案為\(ans\)

考慮這個問題實際上等價於所有的\(e\in E,w(e)\leq ans\)

的邊無法被兩條這樣的鏈完全覆蓋

形象化的描述就是: 這些邊至少分成了 三條岔開的鏈

做法1:

考慮二分答案,把每條\(e\in E,w(e)\leq ans\)的邊的下端點加入,判斷這樣生成的聯通塊是否含有3個葉子即可

複雜度為\(O(n\log n)\)

做法2:

樹形\(\text{dp}\)求解,實際上這個問題就是 (選擇了合法的3條邊中邊權的最大值) 的最小值

考慮類似揹包的做法,每次合併可以加入\((u,v,w)\)這條邊,或者從兒子合併揹包陣列

揹包大小是3,因此複雜度為\(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define reg register
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,const T &b){ ((a>b)&&(a=b)); }
 
char IO;
template <class T=int> T rd(){
    T s=0; int f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}
 
const int N=5e5+10,INF=1e9+10;
 
int n,m;
struct Edge{
    int to,nxt,w;
} e[N*6];
int head[N],ecnt;
void AddEdge(int u,int v,int w) {
    e[++ecnt]=(Edge){v,head[u],w};
    head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
 
int low[N],t[N],id[N],scc,dfn;
int stk[N],top;
void dfs(int u,int f) {
    low[u]=t[u]=++dfn;
    stk[++top]=u;
    erep(u,i) if(v!=f) {
        if(!t[v]) dfs(v,u),cmin(low[u],low[v]);
        else cmin(low[u],t[v]);
    }
    if(low[u]==t[u]){
        int v; ++scc;
        do v=stk[top--],id[v]=scc;
        while(v!=u);
    }
}
 
int head2[N];
void AddEdge2(int u,int v,int w) {
    e[++ecnt]=(Edge){v,head2[u],w};
    head2[u]=ecnt;
}
#define erep2(u,i) for(int i=head2[u],v=e[i].to,w=e[i].w;i;i=e[i].nxt,v=e[i].to,w=e[i].w)
 
int ans=INF;
int dp[N][4],tmp[4];
 // 樹形dp
void dfs1(int u,int f) {
    dp[u][0]=0,dp[u][1]=dp[u][2]=dp[u][3]=INF;
    erep2(u,i) if(v!=f) {
        dfs1(v,u);
        memset(tmp,63,sizeof tmp);
        rep(j,0,3) {
            cmin(tmp[j],dp[u][j]);
            if(j<3) cmin(tmp[j+1],max(dp[u][j],w));
            rep(k,0,3-j) cmin(tmp[j+k],max(dp[u][j],dp[v][k]));
        }
        rep(j,0,3) dp[u][j]=tmp[j];
    }
    cmin(ans,dp[u][3]);
}
 
 
int MT;
int main(){
    n=rd(),m=rd();
    rep(i,1,m) {
        int u=rd(),v=rd(),w=rd();
        AddEdge(u,v,w),AddEdge(v,u,w);
        cmax(MT,w);
    }
    dfs(1,0);
    rep(u,1,n) erep(u,i) if(id[u]!=id[v]) AddEdge2(id[u],id[v],e[i].w);
    // 構建生成樹
    dfs1(id[1],0);
    printf("%d\n",ans==INF?-1:ans);
}