P6004 [USACO20JAN] Wormhole Sort S

https://www.luogu.com.cn/problem/P6004

算法:并查集+二分答案

首先我们可以发现一个性质:
当我们知道用几个虫洞进行排序的时候,我们也会知道

(1)她们用来排序的虫洞宽度的最小值;

(2)那些位置是可以相互到达的。

在这条性质的基础上,我们想到了二分答案

接下来,就是二分答案的条件。

只要判断在开通这些虫洞的情况下,位置i与p_i是否可以相互到达,所以使用并查集。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
int n,m,pos[N],fa[N];
struct Node
{
	int a,b,w;
}e[N];

bool cmp (Node p,Node q)
{
	return p.w>q.w;
}

int read ()
{
	int s=0,k=1;char c=getchar ();
	while (!isdigit (c)) {if (c=='-') k=-1;c=getchar ();}
	while (isdigit (c)) {s=s*10+c-'0';c=getchar ();}
	return k*s;
}

void Init ()
{
	n=read ();m=read ();
	bool flag=true;
	for (int i=1;i<=n;i++)
	{
		pos[i]=read ();
		if (pos[i]!=i) flag=false;
	}
	if (flag==true)
	{
		puts ("-1");
		exit (0);
	}
	for (int i=1;i<=m;i++)
		e[i].a=read (),e[i].b=read (),e[i].w=read ();
}

int Find (int x)
{
	if (x==fa[x]) return x;
	return fa[x]=Find (fa[x]);
}

void Merge (int x,int y)
{
	int r1=Find (x),r2=Find (y);
	if (r1!=r2) fa[r2]=r1;
}

bool check (int nn)
{
	for (int i=1;i<=n;i++)
		fa[i]=i;
	for (int i=1;i<=nn;i++)
		Merge (e[i].a,e[i].b);
	for (int i=1;i<=n;i++)
		if (Find (i)!=Find (pos[i])) return false;
	return true;
}

void Work ()
{
	sort (e+1,e+1+m,cmp);
	int L=1,R=m,ans=-1;
	while (L<=R)
	{
		int mid=(L+R)/2;
		if (check (mid))
		{
			ans=e[mid].w;
			R=mid-1;
		}
		else L=mid+1;
	}
	cout<<ans<<endl;
}

int main ()
{
	Init ();
	Work ();
	return 0;
}

上一篇:快读快写模板


下一篇:PTA 乙级 1078 字符串压缩与解压 (20 分) C++