• 欢迎光临~

CodeForces-300#B 题解

开发技术 开发技术 2022-12-16 次浏览

题意

给定 (n) 个数,保证 (n mid 3),要将这 (n) 个数分配到 (dfrac{n}{3}) 个三元组,有 (m) 个要求 (a,b),每个要求表示 (a,b) 要在同一个三元组里,求最后的分组,若无解则输出 -1。

正文

准备知识:并查集

最坏时间复杂度:(mathcal{O}(n+m))

我们可以利用并查集来处理两个人要在同一组的情况,下面是并查集板子(在下文中,并查集中的每一个区块被称为一棵树):

//板子,不做解释
int ufs[20000];
void init(){
	for(int i=1;i<=n;i++) ufs[i]=i;
}
int fd(int x){
	if(ufs[x]==x)	return x;
	return ufs[x]=fd(ufs[x]);
}
void uoin(int x,int y){
	ufs[fd(x)]=fd(y);
}

但这只是第一步,根据题意,当并查集中存在超过 3 个结点的树,那么这是绝对无解的。那么这要怎么弄呢?我们可以开一个 vector (S_{i,j}) 来储存以 (i) 为根节点的树的节点数 (j),因此我们得到以下代码:

#define pb push_back
vector<vector<int>> s(20000);
for(int i=1;i<=n;i++)	s[fd(i)].pb(i);//插入节点
for(auto &i: s)//判断是否有超过3个节点的树
	if(i.size()>3)	cout<<-1,exit(0);

我们还要考虑如何分组,这是蒟蒻的思路,仅供大佬借鉴。经过上面的无解判定,可以保证并查集中剩下的树只有 1 至 3 个节点。而三个节点的树正好是所需的三元组,无需再分。将有 1 个节点和有 2 个节点的树分别装入不同的 vector (sj,so),可以很好地发现,我们只要将 (sj) 中的某一项和 (so) 的某一项合并,即可得到一个新的三元组(因为两棵树没有任何要求)。

于是有下面的代码:

vector<vector<int>> sj,so,ans;
for(auto &i: s)
	if(i.size()==3)	ans.pb(i);//3个节点的树
	else if(i.size()==2)	so.pb(i);//2个节点的树
	else if(i.size()==1)	sj.pb(i);//1个节点的树
while(!sj.empty()&&!so.empty())
	sj.front().insert(sj.front().begin(),//将so首项插入sj首项末
		so.front().begin(),so.front().end()),
	so.erase(so.begin()),//删去so首项
	ans.pb(sj.front()),//将合并后的sj插入ans末
	sj.erase(sj.begin());//删去sj首项

但,这就完了吗?不!

可以看一下第一个样例:3 0,没错,如果按照上面的代码,所有节点都会被放入 (sj) 中而无法合并,因此我们还要在 (sj)(so) 合并完后,若 (sj) 还有残余且数量大于 3,我们可以每三项每三项合并,于是有:

while(!sj.empty()&&sj.size()>=3){
	int a,b,c;
	auto i=sj.begin();//得到sj首项
	a=i->front(),i=sj.erase(i),//依次将sj的前三项赋值给a,b,c
	b=i->front(),i=sj.erase(i),
	c=i->front(),i=sj.erase(i);
	ans.push_back({a,b,c});
}

当然,如果谨慎些的话,我们还要加上 (sj)(so) 的判断,若仍有残余,则无解。(为什么不合并 (so)?因为它合并就超过 3 个节点了。)

if(so.size()>0)	cout<<-1,exit(0);
if(sj.size()>0)	cout<<-1,exit(0);

最后是输出。

for(auto &i: ans){
	for(auto &j: i)
		cout<<j<<' ';
	cout<<'n';
}

完整 CODE:

//不注解,前面已做讲解,不懂评论区问/私聊
#include<iostream>
#include<vector>
#define pb push_back
using namespace std;
int ufs[20000],n,m;
vector<vector<int>> s(20000),sj,so,ans;
void init(){
	for(int i=1;i<=n;i++) ufs[i]=i;
}
int fd(int x){
	if(ufs[x]==x)	return x;
	return ufs[x]=fd(ufs[x]);
}
void uoin(int x,int y){
	ufs[fd(x)]=fd(y);
}
int main(){
	cin>>n>>m,init();
	int a,b;
	for(int i=1;i<=m;i++)	cin>>a>>b,uoin(a,b);
	for(int i=1;i<=n;i++)	s[fd(i)].pb(i);
	for(auto &i: s)
		if(i.size()>3)	cout<<-1,exit(0);
		else if(i.size()==3)	ans.pb(i);
		else if(i.size()==2)	so.pb(i);
		else if(i.size()==1)	sj.pb(i);
	while(!sj.empty()&&!so.empty())
		sj.front().insert(sj.front().begin(),
			so.front().begin(),so.front().end()),
		so.erase(so.begin()),
		ans.pb(sj.front()),
		sj.erase(sj.begin());
	if(so.size()>0)	cout<<-1,exit(0);
	while(!sj.empty()&&sj.size()>=3){
		int a,b,c;
		auto i=sj.begin();
		a=i->front(),i=sj.erase(i),
		b=i->front(),i=sj.erase(i),
		c=i->front(),i=sj.erase(i);
		ans.push_back({a,b,c});
	}
	if(sj.size()>0)	cout<<-1,exit(0);
	for(auto &i: ans){
		for(auto &j: i)
			cout<<j<<' ';
		cout<<'n';
	}
}

提交记录,完美结束。

后附

日志

v1.0 on 2022.12.16: 发布

程序员灯塔
转载请注明原文链接:CodeForces-300#B 题解
喜欢 (0)