首页 > 其他分享 >CodeForces-300#B 题解

CodeForces-300#B 题解

时间:2022-12-16 23:11:30浏览次数:58  
标签:300 题解 CodeForces sj int erase so front 节点

题意

给定 \(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: 发布

标签:300,题解,CodeForces,sj,int,erase,so,front,节点
From: https://www.cnblogs.com/wanguan/p/16988472.html

相关文章

  • P3874 砍树 题解
    前置树形dp,二分。题意本质上是一个树上背包,需要选不少于\(k\)个物品,每个物品有一个重量\(w\)和价值\(v\),求性价比最大值。分析既然是性价比,显然是分数规划。先......
  • Codeforces Round 838 (Div. 2)
    B.MakeArrayGood题意:给定n个数,每次可以对其中一个数进行操作,其中,在操作数量不超过n的前提下,构造一种操作使得任意两个数中,大的数可以被小的数整除。分析:结论:所......
  • CF992E Nastya and King-Shamans 题解
    传送门分析由于满足\(a_i\ge0\),所以\(s_i\)单调不减。当我们找到一个\(i\)时,不管\(i\)是否满足,下一个可能的一定大于等于\(a_i+s_{i-1}\)。而且\(a_i+s_{i-1}......
  • P8810 [蓝桥杯 2022 国 C] 数组个数 题解
    思路比较简单的一道题。用的五维dp,看到二维和三维的dp直接膜了orz。正文开始。分析不难看出dp。因为\(b_i\)的值只与\(a_{i-1},a_i,a_{i+1}\)有关,所以我们定......
  • CF939F Cutlet 题解
    题意简述有一个正反面都为\(0\)的卡片,每过\(1\)分朝下那一面的数值就会增加\(1\),你可以在几个区间的时间内翻转卡片,求经过\(2n\)秒后能否让这个卡片的正反面的数都......
  • 「Editorial」Codeforces Contest 1149
    C.TreeGenerator™容易发现树上一条路径一定形如))...)((...(。也就是对于任意子段,去掉匹配了的括号后还剩下的部分。而这个东西还是不太好表示,我们有如下引理:这个值......
  • Codeforces Round #837 (Div. 2)
    A.HossamandCombinatorics(CF1771A)题目大意给定一个长度为\(n\)的数组\(a\),问有多少个数对其差的绝对值等于该数组的极差。解题思路若最大值和最小值相等,则答案......
  • Codeforces Round #838 (Div. 2) D
    D.JourneytoUn'Goro题链考虑一个三元组内一定可以排除一个非0的xyz我们询问xz和yz要是gx==gy那么我们的z一定不是0否则gx=pxgy=py排除z要是gx!=gy那么......
  • Educational Codeforces Round 2
    EducationalCodeforcesRound2https://codeforces.com/contest/6003/6:ABDA.ExtractNumbers小模拟。把一个字符分成两部分输出,遇到';'或','视为单词分隔符,非负......
  • YACS 11 月甲题解
    https://iai.sh.cn/contest这把还是简单的,难度对标普及组。所有题解均口胡。T1观察&性质你扫左端点,然后维护以当前左端点最长的合法子段,显然右端点单不降,因为当你......