C135 線段樹分治 P5631 最小mex生成樹

董晓發表於2024-06-09

影片連結:C135 線段樹分治 P5631 最小mex生成樹_嗶哩嗶哩_bilibili

P5631 最小mex生成樹 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

// 線段樹分治 O(nlognlogw)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
#define N 2000005
int n,m,mx,top;
int a[N],b[N],w[N];
int p[N],siz[N];
vector<int>tr[400005]; //節點
struct node{
  int x,y;
}st[N<<1]; //

void insert(int u,int l,int r,int L,int R,int i){
  if(l>R||r<L) return;
  if(L<=l&&r<=R) return tr[u].push_back(i);
  insert(ls,l,mid,L,R,i);
  insert(rs,mid+1,r,L,R,i);
}
int find(int x){ //查詢根
  return p[x]==x?x:find(p[x]);
}
void merge(int x,int y){ //合併集合
  x=find(x),y=find(y);
  if(x==y) return;
  if(siz[x]>siz[y]) swap(x,y);
  st[++top]={x,y};
  p[x]=y;
  siz[y]+=siz[x];
}
void solve(int u,int l,int r){
  int now=top;
  for(auto i:tr[u]) merge(a[i],b[i]);
  
  if(l==r){
    if(siz[find(1)]==n) printf("%d\n",l),exit(0);
  }
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤銷合併
    node s=st[top--];
    p[s.x]=s.x;
    siz[s.y]-=siz[s.x];
  }
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;++i) p[i]=i,siz[i]=1;
  for(int i=1;i<=m;++i){
    scanf("%d%d%d",&a[i],&b[i],&w[i]);
    mx=max(mx,w[i]+1);
  }
  for(int i=1;i<=m;++i){
    insert(1,0,mx, 0,w[i]-1, i);
    insert(1,0,mx, w[i]+1,mx,i);    
  }
  solve(1,0,mx);
}