洛谷 CF1777E Edge Reverse 题解

题目链接

要使最大值最小,那么基本可以确定本题是二分了。先二分最终答案,再判断最大值为 \(x\) 可不可行。由于不可能同一条边两个方向都走,所以可以直接把反转边变成无向边,相当于添加了一条原有边的反向边。加边对于本题来说当然是有利无害的,所以我们可以将边权小于等于 mid 的边全部增添它们的反向边。

接下来就要想办法判断是否存在一个点能到达其他点了。因为增加边后图有很多边,同时可能存在一些环,我们难以较快地找出能到达所有点的点。于是考虑缩点,得到一个 DAG 之后我们就很好处理了,只要除了起点的点入度不为 \(0\) 就可以满足要求。

另外还有一点,缩点后建边时不需要去重,因为我们是要找入度为 \(0\) 的点的个数,而入度为 \(0\) 的点不存在指向它的边,所以不去重边是一样的。之前加了去重边就被卡了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<unordered_map>
using namespace std;
const int N=2e5+100;
int n,m,sbt,u[N],v[N],w[N],l,r,mid,t[N],k,rk,rt[N],b[N],bk,scc[N],cnt,d[N];
bool flag[N];
struct node
{
	int id,last,val;
}a[N*2],ra[N*2];
void add(int a1,int a2)
{
	a[++k].id=a2;
	a[k].last=t[a1];
	t[a1]=k;
}
void radd(int a1,int a2)
{
	ra[++rk].id=a2;
	ra[rk].last=rt[a1];
	rt[a1]=rk;
}
void dfs1(int x)
{
	flag[x]=true;
	for(int i=t[x];i;i=a[i].last)
	{
		if(!flag[a[i].id]) dfs1(a[i].id);
	} 
	b[++bk]=x;
}
void dfs2(int x,int c)
{
	scc[x]=c;
	for(int i=rt[x];i;i=ra[i].last)
	{
		if(!scc[ra[i].id]) dfs2(ra[i].id,c);
	}
}
void kosaraju()
{
	bk=cnt=0;
	for(int i=1;i<=n;i++) flag[i]=false,scc[i]=0;
	for(int i=1;i<=n;i++)
	{
		if(!flag[i]) dfs1(i);
	}
	for(int i=n;i>=1;i--)
	{
		if(!scc[b[i]]) dfs2(b[i],++cnt); 
	}
}
bool check(int x)
{
	int c0=0;
	//unordered_map<int,bool> um[N];
	k=rk=0;
	for(int i=1;i<=n;i++) t[i]=rt[i]=0;
	for(int i=1;i<=m;i++)
	{
		add(u[i],v[i]),radd(v[i],u[i]);
		if(w[i]<=x) add(v[i],u[i]),radd(u[i],v[i]);
	}
	kosaraju();
	for(int i=1;i<=n;i++) d[i]=0;
    //for(int i=1;i<=n;i++) printf("%d",scc[i]);
	for(int i=1;i<=m;i++)
	{
		if(scc[u[i]]!=scc[v[i]]/*&&!um[u[i]].count(v[i])*/)
		{
			//um[u[i]][v[i]]=true;
			d[scc[v[i]]]++;
		}
		if(w[i]<=x)
		{
			if(scc[u[i]]!=scc[v[i]]/*&&!um[v[i]].count(u[i])*/)
			{
				//um[v[i]][u[i]]=true;
				d[scc[u[i]]]++;
			}
		}
	}
	for(int i=1;i<=cnt;i++)
	{
		if(!d[i]) c0++;
	}
	return c0<=1;
}
int main()
{
	scanf("%d",&sbt);
	while(sbt--)
	{
		l=-1,r=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++) 
		{
			scanf("%d%d%d",&u[i],&v[i],&w[i]);
			r=max(r,w[i]+1);
		}
		while(l+1<r)
		{
			mid=(l+r)>>1;
			if(check(mid)) r=mid;
			else l=mid;
			//printf("%d %d\n",l,r);
		} 
		check(r)?printf("%d\n",r):printf("-1\n");
        //cout<<check(30001);
	}
	return 0;
}
posted @ 2024-09-30 13:47  MinimumSpanningTree  阅读(12)  评论(0)    收藏  举报