[CF1027F]Session in BSU

题目

传送门 to luogu

思路

将每场考试看成一条边,连接 a i a_i b i b_i ,那么有如下转化:只有基环树和树满足条件。结论是显然的,因为每场考试(边数)都需要有一天来考,所以不能超过总天数(点数),也就是 E V |E|\le |V|

有了这一点,我们就可以直接用并查集进行判断了。对于一颗树,我们将其根作为该树内权值最大的点(以尽量更早考完所有考试)。对于基环树,我们打一个特殊标记即可,比如将其与 0 0 相连。

值域很大怎么办?用个 m a p \tt map 就完事儿了。复杂度 O ( n log n ) \mathcal O(n\log n) 。注意并查集也是这个复杂度——因为我的实现方法不允许按秩合并。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 1000005;
int val[MaxN<<1]; // 并查集节点->真实值
int ans; // 最终答案

namespace UFS{
	int fa[MaxN<<1];
	void init(int n){
		for(int i=0; i<=n; ++i)
			fa[i] = i;
	}
	inline int find(int a){
		if(a == fa[a]) return a;
		return fa[a] = find(fa[a]);
	}
	bool combine(int a,int b){
		a = find(a), b = find(b);
		if(!a && !b) return false;
		if(!a || !b){ // 一个基环一个树
			ans = max(ans,val[a+b]);
			fa[a+b] = 0; return true;
		}
		if(a == b){ // 形成自环
			ans = max(ans,val[a]);
			fa[a] = 0; return true;
		}
		if(val[a] > val[b])
			swap(a,b);
		ans = max(ans,val[a]);
		fa[a] = b; return true;
	}
}

map< int,int > mp;
int main(){
	int n = readint(), tot = 0;
	UFS::init(n<<1);
	for(int i=1; i<=n; ++i){
		int a = readint(), b = readint();
		if(!mp.count(a))
			val[mp[a] = ++ tot] = a;
		if(!mp.count(b))
			val[mp[b] = ++ tot] = b;
		if(!UFS::combine(mp[a],mp[b])){
			puts("-1"); return 0;
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108087104