Subtask 1

对于 20% 的数据 $n , m \leq 10$

直接搜索即可,没有太多的细节

期望得分:$20$

Subtask 2

数据保证为一棵树

这时,我们只需将叶子节点随机配对。随机配对并不能够保证覆盖所有的点,这时就需要我们进行调整

对于每次调整,我们只需要找到一个未被覆盖的点作为 root(细节:该点不为叶子节点且至少有两个子树,否则它一开始就能被覆盖到)

任意取它的两个子树,并从中分别挑选两条路径 $(u,v)$ $(u',v')$ 将其调整成 $(u,v')$ $(v,u')$。

画图可知,这时 $root$ 一定被覆盖到,故可知在不超过 $n$ 步内可以完成调整

结合算法 $1$ ,期望得分:$40$

Subtask 3

从 Subtask2 扩展到一般情况

我们找出图中的 dfs 树(不存在横插边),然后再结合算法2即可

期望得分:$100$

感谢 ducati 老师教会了我

参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1010;

inline int read() { //速读 
	int f = 1 , ret = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c <= '9' && c >= '0') {
		ret = (ret << 3) + (ret << 1) + c - 48;
		c = getchar();
	}
	return ret * f;
}

int cnt , Head[MAXN];

struct Edge {
	int Next , To;
}Edge[MAXN << 1]; 

void Add(int u , int v) {
	cnt++;
	Edge[cnt].To = v;
	Edge[cnt].Next = Head[u];
	Head[u] = cnt;
} //前向星存图 (20 ~ 31)

vector<int> G[MAXN] , res;

bool Vis[MAXN] , Covered[MAXN] , book[MAXN];
int Leaf[MAXN] , Deg[MAXN]; //存叶节点和每个点的度
int N , M , Len , Rest , Extra; 
int Pair[MAXN];
//Pair[i] = j表示把(i , j)配成一对
//配对是互相的 

void Get_Dfs_Tree(int now) { //找dfs树 
	Vis[now] = true; //标记
	for(int i = 0;i < G[now].size();i++) { //vector存图 
		int v = G[now][i];
		if(!Vis[v]) {
			Get_Dfs_Tree(v);//以v为root的子树递归求解
			Deg[now]++;
			Deg[v]++; //更新两点的度
			Add(now , v);
			Add(v , now); //加入前向星 
		}
	} 
}

bool Dfs(int now , int dv , int father , int flag) {
	if(now == dv) {
		Covered[now] = true;
		if(flag == true && now != Extra) res.push_back(now);
		return true;
	}
	bool Val = false;
	for(int u = Head[now] ; u ; u = Edge[u].Next) {
		int v = Edge[u].To;
		if(v == father) continue;
		if(Dfs(v , dv , now , flag)) {
			Val = true;
			Covered[now] |= 1;
		}
	}
	if(flag == 1 && Val == 1 && now != Extra) res.push_back(now);//特判now != Extra 
	return Val;
}

int Get_Leaf(int now , int father) {
	if(Deg[now] == 1) return now; // 度为一的点——叶子 
	for(int u  = Head[now] ; u ; u = Edge[u].Next) {
		int v = Edge[u].To;
		if(v != father) return Get_Leaf(v , now); //递归子树 
	}
}

void Make_Pair(int u , int v) {
	Pair[u] = v;
	Pair[v] = u;
	//配对是互相的 
	Dfs(u , v, 0 , 0);
} 

void print(int u,int v){
	res.clear();
	Dfs(u , v , 0 , 1);
	Rest--;
	printf("%d ",res.size());
	for (int i = 0 ; i < res.size() ; i++){
		printf("%d ",res[i]);
	}
	puts("");
}

int main() {
	N = read();
	M = read();
	Rest = (N + 5) / 6;
	for(int i = 1;i <= M;i++) {
		int u = read();
		int v = read();
		G[u].push_back(v);
		G[v].push_back(u); //vector存图 
	}
	Get_Dfs_Tree(1); //将1作为根 
	for(int i = 2;i <= N;i++) { //细节:从i : 2 -> N ,因为1是根,可能存在1->叶子的边 
		if(Deg[i] == 1)
			Leaf[++Len] = i; //叶子节点集合 
	}
	if(Len >= N / 3) { //处理 b6e0 
		puts("2");
		for(int i = 1;i <= N / 3;i++) {
			cout << Leaf[i] << " ";
		}
		return 0;
	}
	if(Deg[1] == 1) Leaf[++Len] = 1;  //单独处理root 
	if(Len % 2 == 1) { // 若Len为奇数,那么我们连一条从 1 到 n+1 的边,增加一个叶节点,输出时特判 
		Add(1 , ++N);
		Deg[N] = 1;
		Leaf[++Len] = N;
		Extra = N; //特判时使用 
	}
	for(int i = 1;i <= Len;i += 2) { //为了方便,写124行 
		Make_Pair(Leaf[i] , Leaf[i + 1]); //配对 
	}
	while(true) {
		int now , Cnt_Son = 0 , u1 , v1 , u2 , v2 , done = 1;
		for(int i = 1;i <= N;i++) {
			if(!Covered[i]) {
				now = i;
				done = false;
				break;
			}
		}
		if(done) break;
		for(int i = Head[now] ; i ; i = Edge[i].Next) {
			int j = Edge[i].To;
			Cnt_Son++;
			if(Cnt_Son == 1) {
				u1 = Get_Leaf(j , now);
				v1 = Pair[u1]; 
			}
			else if(Cnt_Son == 2) {
				u2 = Get_Leaf(j , now);
				v2 = Pair[u2]; 
			}
			else break;
		}
		Make_Pair(u1 , v2);
		Make_Pair(v1 , u2);
	} 
	puts("1");
	for(int i = 1;i <= Len;i++) {
		int u = Leaf[i];
		int v = Pair[Leaf[i]];
		if(u < v) print(u , v);
	}
	for(int i = 1;i <= Rest;i++)
		cout << 1 << " " << i << endl; 
	return 0;
}