Luogu P5234 [JSOI2012] 越狱老虎桥 题解 [ 紫 ] [ Tarjan ] [ 边双连通分量 ] [ 树的直径 ] [ 二分 ]
越狱老虎桥:边双好题。一开始还以为是恶心的分讨,后面才发现是要求一个式子。
观察
首先我们注意到下面两个条件:
- 对方先在图上加一条边,这条边我们在下一步的时候不能断。
- 我们可以随意在图上断一条边。
要求求出对方断任何边后答案的最大值。
一个错误的理解是,我们在断完边后,对方再来根据我们方案加边。我一开始的做法就是边双缩点之后找以 \(1\) 为根的菊花来贪心。但是这样的做法是错的,为啥呢?因为对方是在不知道我们操作的情况下加边的,而我们是知道对方的操作下断边的。这就导致了对方能对着我们断的边重新加回来,那怎么搞都是无解了。。。。
所以正确的理解应该是对于每一种可能的加边情况,求出 \(\max(\min res)\)。\(\min res\) 是指加边后局面为 \(res\) 的最少所需人数。
然后边双缩点是显然的,因为这题只有断割边才有用,断一条 EDCC 内的边就相当于没断。
于是我们就可以在树上考虑求这个式子了。
实现
在所有情况里使得最小值最大,是个很典的二分答案的形式,于是我们考虑二分最大值,check 是否有合法的情况。
那么加边的操作如何刻画呢?显然就是找到树上的一条链,答案是除去这条链上的边,其他边的最小边权。
既然要让其他边最小值是 \(mid\),就得让小于 \(mid\) 的所有边都包含在这条链上,也就是让链上尽可能多地包含小于 \(mid\) 的边。所以就可以把小于 \(mid\) 的边边权重新赋值为 \(1\),其余边赋值为 \(0\),跑树的直径板子,如果直径等于小于 \(mid\) 的边的数量,就说明这个情况合法。二分找到最后一个合法情况即可。
时间复杂度 \(O(n+m+n \log n)\)。无解的情况就是缩点形成的树是一条链的情况或者一点特殊的 corner,这时候可以在链的两端连边使得整张图成为一个 EDCC,就没法断边了。
代码
注意 EDCC 缩点的时候,形成的树上能连边当且仅当两个点处于不同的 EDCC 中且这两点之间有边。
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int N=500005,M=2000005;
using pi=pair<int,int>;
int n,m;
int h[N],idx=1;
struct Edge{
int v,ne,w;
}e[M];
void add(int u,int v,int w)
{
e[++idx]={v,h[u],w};
h[u]=idx;
}
void addeg(int u,int v,int w)
{
add(u,v,w);
add(v,u,w);
}
int dfn[N],low[N],tp,stk[N],tot,cnt,edcc[N],sz[N],sm,dp[N],diam;
void tarjan(int u,int ineg)
{
dfn[u]=low[u]=++tot;
stk[++tp]=u;
for(int i=h[u];i;i=e[i].ne)
{
int v=e[i].v;
if(dfn[v]==0)
{
tarjan(v,i);
low[u]=min(low[u],low[v]);
}
else if(i^ineg^1)
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
cnt++;
int x;
do{
x=stk[tp--];
edcc[x]=cnt;
sz[cnt]++;
}while(x!=u);
}
}
vector<pi>g[N];
int x;
void getdiam(int u,int f)
{
dp[u]=0;
for(auto eg:g[u])
{
int v=eg.fi,w=eg.se;
if(v==f)continue;
if(w<x)sm++,w=1;
else w=0;
getdiam(v,u);
diam=max(diam,dp[u]+dp[v]+w);
dp[u]=max(dp[u],dp[v]+w);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
while(m--)
{
int u,v,w;
cin>>u>>v>>w;
addeg(u,v,w);
}
for(int i=1;i<=n;i++)if(dfn[i]==0)tarjan(i,0);
for(int u=1;u<=n;u++)
{
int edccu=edcc[u];
for(int i=h[u];i;i=e[i].ne)
{
int v=e[i].v,w=e[i].w;
int edccv=edcc[v];
if(edccu!=edccv)g[edccu].push_back({edccv,w});
}
}
sm=diam=0;
x=10000000;
getdiam(1,0);
if(diam==cnt-1)
{
cout<<-1;
return 0;
}
int l=0,r=100005,mid;
while(l<r)
{
mid=(l+r+1)>>1;
sm=diam=0;
x=mid;
getdiam(1,0);
if(diam>=sm)l=mid;
else r=mid-1;
}
if(l>100000)cout<<-1;
else cout<<l;
return 0;
}

浙公网安备 33010602011771号