P6004
USACO 2020 Jan 银组 T3 Wormhole Sort
题意
给定 \(n\) 的一个排列 \(p\),有 \(m\) 个传送门可以交换 \(x_i,y_i\) 处的两个数,分数为 \(w_i\)。求使得 \(p\) 有序的最小分数的最大值。\(n,m\leq 10^5,x_i,y_i\leq n,w_i\leq 10^9\).
题解
观察到一个传送门只要使用就可以无限使用,考虑并查集,一个集合内可以互相换。还有一个性质就是当一个传送门使用了,那么分数比它大的都可以使用。可以二分分界点,当然也可以枚举。但是不用每次都挨个枚举,前面的能满足条件后面加多少传送门它还是满足条件。复杂度 \(O(n\log n)\),瓶颈在排序。aclink。
拓展:变式是求最大的分数和,很明显就是最大生成树,跑一遍 Kruskal 或者 Prim 就行。
坑
- 记得判
-1。
代码
#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)
using namespace std;
const int N=1e5+5;
void solve();
int n,m,a[N],fa[N];
struct wor{
int x,y,z;
bool operator< (wor A){
return z>A.z;
}
} w[N];
signed main(){
int Test=1;
// scanf("%d",&Test);
while(Test--) solve();
return 0;
}
int find(int x){
if(x==fa[x]) return x;
fa[x]=find(fa[x]);
return fa[x];
}
void solve(){
scanf("%d%d",&n,&m);
bool ok=0;
L(i,1,n,1){
scanf("%d",a+i);
if(i!=a[i]) ok=1;
fa[i]=i;
}
L(i,1,m,1){
scanf("%d%d%d",&w[i].x,&w[i].y,&w[i].z);
}
if(!ok){
printf("-1\n");
return;
}
sort(w+1,w+m+1);
int j=1;
L(i,1,m,1){
int fx=find(w[i].x),fy=find(w[i].y);
if(fx!=fy){
fa[fx]=fy;
}
while(find(j)==find(a[j])){
j++;
if(j>n){
printf("%d\n",w[i].z);
return;
}
}
}
}
本文来自博客园,作者:jess1ca1o0g3,转载请注明原文链接:https://www.cnblogs.com/jess1ca1o0g3/p/19056795

浙公网安备 33010602011771号