Loading

P6004

USACO 2020 Jan 银组 T3 Wormhole Sort

link

题意

给定 \(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;
      }
    }
  }
}
posted @ 2025-08-25 13:33  jess1ca1o0g3  阅读(5)  评论(0)    收藏  举报