题意:
T组数据,每组数据给你n(<=1e5)个点,m(<=min(1e5,n*(n-1)/2))条无向边,你需要构造一个合法的序列,其中每一项输出三个数:
点x,你选择的点集的大小,然后给出这些点。(初始为空图,之后系统会增加一个新点x,并将x与你给出的这些点所在的连通块的每一个结点连边!)
最后形成题中给你的图。问你是否存在合法方案,如果存在输出Yes和任意一种方案,否则输出No。
思路:
直接正着做找合法序列太难了,我们考虑从原图往下一个一个拆点,拆成一个合法序列,最后倒着输出。
注意到我们拆这一个点u,它的度数 必须 等于 所在连通块的大小 -1 。
因此我们将所有点按度数从小到大排序,先用并查集(注意,这里用的是可撤销并查集,因为要拆点会造成连通块数变多,不能路径压缩!!!)建立初始的连通块,记录度数大的点向度数小的点连的边。
之后再从度数最大的点开始拆,一直拆到度数最小的点(显然度数最大的点一定是后面才添加进图里去的)。
对于某个点u,如果它的度数d[u] != u所在的连通块的大小 -1 则答案一定不存在。否则u点便可以拆下来。
然后更新u所连的所有点的度数(显然连的点的度数一定小于等于u的度数)。然后撤销u向度数小的点连的边,记录答案。
并查集这一块还是用的不够熟练,还需要多加练习。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int maxn=1e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k;
int mp[maxn];
int tmp,cnt;
int flag;
bool ok[maxn];
vector<int>vc[maxn];
template <typename T>
inline void read(T &X){
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
struct node{
int x,y,fax,szx,hx;
}stk[maxn];
struct st{
int id;
int v;
}d[maxn];
struct BCJ{
int fa[maxn];
int sz[maxn];
int h[maxn];
void init(int n){
cnt=0;
rep(i,0,n){
fa[i]=-1;
sz[i]=1;
h[i]=0;
}
}
int find(int x){return fa[x]==-1?x:find(fa[x]);}
int un(int ox,int oy){
int x=find(ox);
int y=find(oy);
if(x==y) return 0;
if(h[x]>h[y]) swap(x,y);
if(h[x]==h[y]) h[y]++;
stk[++cnt]=node{x,y,fa[x],sz[x],h[x]};
fa[x]=y;
sz[y]+=sz[x];
return 1;
}
void undo(int num){
while(num--){
node k=stk[cnt--];
h[k.x]=k.hx;
sz[k.y]-=k.szx;
fa[k.x]=k.fax;
}
}
int get(int x){
x=find(x);
return sz[x];
}
}bcj;
bool cmp(st x,st y){
return x.v<y.v||(x.v==y.v&&x.id<y.id);
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("D:/Temp/in.txt", "r", stdin);
#endif
int T,cas=1;
read(T);
while(T--)
{
//cout<<" &^% "<<endl;
read(n);read(m);
bcj.init(n);
rep(i,1,n) {
vc[i].clear();
d[i].v=0;
d[i].id=i;
ok[i]=false;
}
rep(i,1,m){
int u,v;
read(u);read(v);
vc[u].push_back(v);
vc[v].push_back(u);
d[u].v++;d[v].v++;
}
sort(d+1,d+1+n,cmp);
vector<vector<int> >tp(n+1);
//cout<<" *&(^^ "<<endl;
for(int i=1;i<=n;i++){
int u=d[i].id;
mp[u]=i;
for(int j=0;j<vc[u].size();j++){
int v=vc[u][j];
if(!ok[v]) continue;
if(bcj.un(u,v)){
tp[u].push_back(v);
}
}
ok[u]=true;
}
//cout<<" *&(^^ "<<endl;
vector<pair<int,vector<int> > >ans;
int fg=1;
dep(i,n,1){
int u=d[i].id;
//cout<<u<<" "<<d[i].v<<" "<<bcj.get(u)-1<<endl;
if(d[i].v!=bcj.get(u)-1) {
puts("No");
fg=0;
break;
}
for(int j=0;j<vc[u].size();j++){
int v=vc[u][j];
d[mp[v]].v--;
}
ans.push_back(make_pair(u,tp[u]));
bcj.undo(tp[u].size());
}
if(!fg) continue;
puts("Yes");
reverse(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++){
int nm=ans[i].first;
vector<int> mm=ans[i].second;
int msz=mm.size();
printf("%d %d",nm,msz);
for(int j=0;j<msz;j++){
printf(" %d",mm[j]);
}
puts("");
}
}
return 0;
}