1. 程式人生 > >[CF1027F]Session in BSU[最小基環樹森林]

[CF1027F]Session in BSU[最小基環樹森林]

題意

\(n\) 門課程,每門課程可以選擇在 \(a_i\) 或者 \(b_i\) 天參加考試,每天最多考一門,問最早什麼時候考完所有課程。

\(n\leq 10^6\)

分析

  • 類似 [BZOJ4883]棋盤上的守衛 一題。

  • 將每門課程對應的兩天連邊,如果課程 \(i\) 要在第 \(x\) 天考,則對應邊指向 \(x\)

  • 最後有 \(n\) 天,\(n\) 條有向邊,每條邊的入度為1,構成了基環樹森林。

  • 總時間複雜度為 \(O(nlogn)\) (離散化)。

總結:這種 \(A\) 可以選擇兩種物品之一,每種物品最多被一個人選擇的問題可以考慮基環樹。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
typedef long double ld;
inline int gi(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
  return x*f;
}
template<typename T> inline bool Max(T& a, const T& b) {
    return a < b ? a = b, true : false;
}
template<typename T> inline bool Min(T& a, const T& b) {
    return a > b ? a = b, true : false;
}
const int N=1e6 + 7;
int n,len,ans;
int V[N<<1],par[N<<1],cir[N<<1],mx[N<<1],a[N],b[N];
int getpar(int a){return par[a]==a?a:par[a]=getpar(par[a]);}
int main(){
  n=gi();
  rep(i,1,n+n) par[i]=i;
  rep(i,1,n) V[++len]=a[i]=gi(),V[++len]=b[i]=gi();
  sort(V+1,V+1+len);
  len=unique(V+1,V+1+len)-V-1;
  rep(i,1,n){
    a[i]=lower_bound(V+1,V+1+len,a[i])-V;
    b[i]=lower_bound(V+1,V+1+len,b[i])-V;
    int f1=getpar(a[i]),f2=getpar(b[i]);
    if(cir[f1]&&cir[f2]) return puts("-1"),0;
    if(f1>f2) swap(f1,f2);Max(ans,V[f1]);
    if(f1==f2) {cir[f2]=1;continue;}
    par[f1]=f2,cir[f2]|=cir[f1];
  }
  printf("%d\n",ans);
  return 0;
}