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;
}
posted @ 2025-04-20 14:36  KS_Fszha  阅读(27)  评论(0)    收藏  举报