【Atcoder】AtCoder Grand Contest 059 B - Arrange Your Balls | 图论、构造

1,046 阅读2分钟

【Atcoder】AtCoder Grand Contest 059 B - Arrange Your Balls | 图论、构造

题目链接

B - Arrange Your Balls (atcoder.jp)

题目

image.png

题目大意

nn 个球,第 i(1in)i(1\le i \le n) 个球的颜色是 cic_i。把 nn 个球排成一个环,如果相邻两个球的颜色不同,那么他们将会构成一个二元组 (cx,cy)(c_x,c_y),最小化二元组的种类数,输出排列方案。

(1,2)(1,2)(2,1)(2,1) 属于一个种类。

思路

假设有 mm 种不同的颜色。

容易想到可以把所有颜色相同的球放在一起,将所有球排成一个环。则每种颜色都会参与两个二元组的形成,答案为 mm

是否存在更小的答案呢?

想象我们构建一棵有 mm 个节点的树,每个节点代表一种颜色,我们对这棵树进行 DFS,每经过一条树边,我们就输出边指向的点的颜色。这样每个二元组都会与一条树边进行对应。

image.png

观察示例我们发现,用建树的方法来构建环时,树边一共被经过 2×m22\times m-2 次,且每种颜色的的球至少出现了度数次。(如果建出的树某点的度数小于对应颜色球的数量,只需要连续输出多次即可。)

具体来说,令 cnt[i]cnt[i] 表示第 i(1im)i(1\le i\le m) 种颜色的球的个数,只有在满足条件

[i=1mmin(m1,cnt[i])]2×m2[\sum_{i=1}^m {min(m-1,cnt[i])}]\ge 2\times m-2

时,才能建出树来。

怎么建树呢?首先将所有点都压进待合并的集合 SS 中,进行 n1n-1 次下述操作:

  • 找到 SS 中度数最小的点 vv
  • 找到 SS 另一点 uu,满足:若 SS 中点的数量大于 22uu 的度数应大于 11
  • uuvv 之间建边。
  • 将点 uu 的度数减小 11
  • 从集合 SS 中删除点 vv

建出树后我们对树进行一遍 DFS,输出答案即可。

代码

#include <stdio.h>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
const int N=200001;
int n,m,k,tot;
using LL=long long;
int a[N],cnt[N];
vector<int> e[N];
struct asdf{
	int col,num;
	bool operator < (const asdf a) const
	{
		return num<a.num;
	}
}q[N];
void dfs(int u,int fa)
{
	for (auto v:e[u])
	{
		if (v==fa) continue;
		printf("%d ",v);
		cnt[v]--;
		dfs(v,u);
		printf("%d ",u);
		cnt[u]--;
	}
	while (cnt[u]--) printf("%d ",u);
}
void solve()
{
	m=0;
	for (int i=1;i<=n;++i) cnt[i]=0,e[i].clear();
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&a[i]),cnt[a[i]]++;
	for (int i=1;i<=n;++i)
		if (cnt[i]) q[++m]={i,cnt[i]};
	LL sum=0;
	for (int i=1;i<=n;++i) sum+=min(m-1,cnt[i]);
	if (sum<2*m-2)
	{
		sort(a+1,a+1+n);
		for (int i=1;i<=n;++i) printf("%d ",a[i]);
		printf("\n");
		return;
	}
	sort(q+1,q+1+m);
	int t=1;
	while (q[t].num==1) t++;
	for (int i=1;i<m;++i)
	{
		if (i==t) t++;
		e[q[i].col].push_back(q[t].col);
		e[q[t].col].push_back(q[i].col);
		q[t].num--;
		if (q[t].num==1&&t!=m) t++;
	}
	dfs(q[m].col,0);
	printf("\n");
}
int main()
{
	int T;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}