Codeforces 1868D. Flower-like Pseudotree

题目链接:D - Flower-like Pseudotree

题目大意:给定度数数组 \({d_n}\),要求构造一个 \(n\) 个点 \(n\) 条边的连通图(也就是基环树),允许有重边,但不能有自环。需要满足第 \(i\) 个点的度数恰好为 \(d_i\),并且将环上的边全部删去后,剩下的每棵树的高度(以原先在环上的点为根)相同。

首先考虑几个特殊情况:

  • \(\sum d_i\neq 2n\) 时,无解
  • \(\forall i,d_i=2\),则直接输出一个大小为 \(n\) 的环

将构造分两步完成,先将非叶子结点全部安排好以完成深度限制,然后再把所有叶子结点连到这些点上以符合度数限制,其中后者做法平凡。基于这一点考虑,如果不存在度数为 \(2\) 的点,那么直接把所有非叶子结点连成一个环就能完成第一步构造。剩下的情况都是必然存在一个度数为 \(2\) 的点的。

接下来,考虑在环上延伸出点以完成构造。这时发现由于在环上的点度数必须 \(\gt 2\),而环的大小至少为 \(2\)。所以若度数 \(>2\) 的点数为 \(1\) 则无解。显然,若不在环上的非叶子结点个数是环大小的倍数,那么直接在环上挂几条长度相等的链即可。于是对环长为 \(2\) 的情况进行讨论。

若钦定了环长为 \(2\),那么只需要判断剩余的非叶子结点个数是否为偶数,若是则直接可以完成构造。若不是,那我们先尽量让两边的链长平均,这样有一边会多出来一个点 \(x\) ,考虑将这个多出来的点移到某个深度比 \(x\) 小的点 \(y\) 后面,让他作为 \(y\) 的儿子。

此时,\(dep(x)=dep(y)+1\),于是只需保证在删去\(x\) 后,\(y\) 不是当前基环树上的叶子结点即可。那么可以在一开始放链的时候,优先让 \(d_i\) 大的点先连(深度更小),这样 \(x\) 在找爹的时候,直接找深度最浅的有空位的父亲即可,这样做一定是较优的。

接下来说明用上述方法无法完成构造时,一定无解,不感兴趣可跳过。

先考虑无法构造时,是什么样的一种情况。

此时一定是 \(x\) 在找爹时,找不到一个比当前爹深度更浅的,且有空位的新爹了。

由于我们是优先连 \(d_i\) 更大的点,所以此时所有的点一定都没有空位,那么显然除了和当前 \(x\) 的父亲深度一样的两个点,其它点的 \(d_i\) 一定为 \(2\),而环上的两个点 \(d_i=3\)

\(x\) 的父亲深度 \(\gt 1\)(环上的点深度看做 \(0\))时,深度为 \(1\) 的点没有空位,那么其 \(d_i\) 一定为 \(2\)。对应非叶子结点的 \(d_i\) 可重集的情况就是 \(\{3,3,2,2,2,\dots,2,2\}\),其中 \(2\) 的个数为奇数。那么显然此时只能有两个点在环上,因为有叶子时度数为 \(2\) 的点不能在环上。于是无法通过调整环长来求解,对应情况无解。

\(x\) 的父亲深度为 \(1\) 时,此时是深度为 \(0\) 的点没有空位,对应情况则为 \(\{3,3,2\ or\ 3,2\ or\ 3,2\}\)。此时虽然可以调整环长,但是调整环长后剩下的非叶子结点不足以连出一条长度为 \(1\) 的链,所以也无解。

\(x\) 的父亲深度为 \(0\) 时,那么此时情况为 \(\{G_1,G_2,2\}\)\(G_i\) 表示某个 \(\gt 2\) 的数,与 \(x\) 的父亲深度 \(\gt 1\) 的情况一样,也是无解的。

那么完成几个特殊情况的判断后,贪心连链即可。由于需要找新爹的点最多只会有一个,所以直接按度数从大到小找到一个满足条件的点就好。

#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define GG {printf("No\n");return;}
int T,n,fa[N],dep[N];
struct rua
{
	int deg,id;
	void read(int i){scanf("%d",&deg),id=i;}
	bool operator <(const rua &t)const{return deg>t.deg;}
}a[N];
void init()
{	
	int d2=0,d1=0;
	long long sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		dep[i]=fa[i]=0;
		a[i].read(i);
		if(a[i].deg==1)d1++;
		if(a[i].deg==2)d2++;
		sum+=a[i].deg;
	}
	if(sum!=2*n)GG
	if(n-d1-d2==1)GG
	if(d2==n){
		printf("Yes\n");
		for(int i=1;i<=n;i++)
			printf("%d %d\n",i,i%n+1);
		return;
	}
	sort(a+1,a+n+1);
	if(d2==0){
		printf("Yes\n");
		vector<int>d;
		int id=1;
		while(id<=n && a[id].deg>1)id++;id--;
		for(int i=1;i<=id;i++){
			printf("%d %d\n",a[i].id,a[i%id+1].id);
			for(int j=1;j<=a[i].deg-2;j++)d.push_back(a[i].id);
		}
		for(int i=id+1;i<=n;i++)
			printf("%d %d\n",a[i].id,d[i-id-1]);
		return;
	}
	int lst=0;
	a[1].deg-=2,a[2].deg-=2;
	for(int i=3;a[i].deg>=2;i++){
		a[i].deg--;
		fa[a[i].id]=a[i-2].id;
		dep[a[i].id]=dep[fa[a[i].id]]+1;
		a[i-2].deg--;
		lst=i;
	}
	if(lst&1){
		int f=0;
		for(int i=1;i<lst;i++)
			if(a[i].deg && dep[a[i].id]<dep[fa[a[lst].id]]){
				f=1;
				a[lst-2].deg++;
				fa[a[lst].id]=a[i].id;
				a[i].deg--;
				break;
			}
		if(f==0)GG
	}
	vector<int>d;
	for(int i=1;i<=lst;i++)
		for(int j=1;j<=a[i].deg;j++)
			d.push_back(a[i].id);
	for(int i=lst+1;i<=n;i++)
		fa[a[i].id]=d[i-lst-1];
	printf("Yes\n");
	for(int i=1;i<=2;i++)
		printf("%d %d\n",a[i].id,a[i%2+1].id);
	for(int i=3;i<=n;i++)
		printf("%d %d\n",a[i].id,fa[a[i].id]);
}
int main()
{
	scanf("%d",&T);
	while(T--)init();
}
posted @ 2023-09-23 19:55  DeaphetS  阅读(29)  评论(0编辑  收藏  举报