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; }