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",°),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();
}