CF 1027 F. Session in BSU
F. Session in BSU
https://codeforces.com/contest/1027/problem/F
题意:
n场考试,每场可以安排在第ai天或者第bi天,问n场考完最少需要多少天。
分析:
将所有的a与b连边,一条边相当于一场考试,一个点相当于一个考试时间,每条边需要找一个点。
那么在一个联通块中,边数>点数,无解(这些考试都只能在这个联通块内的点考)。
如果边数=点数,那么相当于出现了环,每条边和一个点匹配,即n场考试,n个考试时间,所以这个联通块内的答案就是最大的点。
如果边数<点数,那么一定是一棵树,那么所有的最大的时间点就可以不选了,这个联通块内的答案就是次大值。
并查集维护加边,与当期联通块的情况。加入一个条边,合并两个联通块,对新联通块维护最大值和次大值。
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #include<iostream> 6 #include<cctype> 7 #include<set> 8 #include<vector> 9 #include<queue> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 2000005; 20 21 struct Node{ 22 int a,b; 23 }A[N >> 1]; 24 int Num[N], fa[N], fir[N], sec[N], tag[N]; 25 26 int find(int x) { 27 return x == fa[x] ? x : fa[x] = find(fa[x]); 28 } 29 int main() { 30 int n = read(), cnt = 0; 31 for (int i=1; i<=n; ++i) { 32 A[i].a = read(), A[i].b = read(); 33 Num[++cnt] = A[i].a, Num[++cnt] = A[i].b; 34 } 35 sort(Num + 1, Num + cnt + 1); 36 int lim = cnt;cnt = 1; 37 for (int i=1; i<=lim; ++i) if (Num[i] != Num[cnt]) Num[++cnt] = Num[i]; 38 for (int i=1; i<=n; ++i) { 39 A[i].a = lower_bound(Num + 1, Num + cnt + 1, A[i].a) - Num; 40 A[i].b = lower_bound(Num + 1, Num + cnt + 1, A[i].b) - Num; 41 } 42 for (int i=1; i<=cnt; ++i) fa[i] = i, fir[i] = Num[i]; 43 for (int i=1; i<=n; ++i) { 44 int u = find(A[i].a), v = find(A[i].b); 45 if (u != v) { 46 fa[v] = u; tag[u] |= tag[v]; 47 if (fir[v] > fir[u]) sec[u] = max(fir[u], sec[v]), fir[u] = fir[v]; 48 else sec[u] = max(sec[u], fir[v]); 49 } 50 else if (tag[u]) { 51 puts("-1"); return 0; 52 } 53 else tag[u] = 1; 54 } 55 int Ans = 0; 56 for (int i=1; i<=cnt; ++i) { 57 if (i == find(i)) { 58 if (tag[i]) Ans = max(Ans, fir[i]); 59 else Ans = max(Ans, sec[i]); 60 } 61 } 62 cout << Ans; 63 64 return 0; 65 }