题目链接:Cover
题意
在一个 n n 个节点 条边的图中,用最少的一笔画把图上所有的边都覆盖一次,问最少要画多少划,并输出方案。
输入
多组输入(不超过 20 20 组),每组输入第一行为两个整数 n,m (1≤n,m≤105) n , m ( 1 ≤ n , m ≤ 10 5 ) ,接下去 m m 行每行两个整数 ,表示在节点 u u 和 之间有一条路径,数据保证没有重边与自环。
输出
对于每组输入,第一行输出一个整数 p p ,表示最少的笔画数,接下去 行,每行表示一条路径,每行的第一个数字 x x 表示这条路径经过的边的数量,后面跟着 个整数,每个整数表示经过编号为 abs(x) a b s ( x ) 的边,如果经过第 i i 条路径时是从 到达 vi v i (路径方向与输入相同),则输出 i i ,否则(路径方向与输入相反)输出 ,路径需要按照笔画顺序输出。如果有多组输出任意一组。
样例
输入 |
---|
3 3 1 2 1 3 2 3 |
输出 |
1 3 1 3 -2 |
题解
对每个连通块分开处理,如果某个连通块内只有一个点,则不需要笔画,否则统计这个连通块内度为奇数的点的数量 cnt c n t ,则需要的最少笔画数量为 max(cnt2,1) max ( c n t 2 , 1 ) (欧拉路径性质),求和就是整张图的最少笔画数。然后对于每个连通块内度为奇数的点(点的个数一定为偶数),两两加边,这样就能使得所有点的度都为偶数,在新图上跑出欧拉路径,再将这个路径上之前加上去的边从环上删掉,就可以得到 max(cnt2,1) max ( c n t 2 , 1 ) 条路径。
过题代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <algorithm>
#include <sstream>
using namespace std;
#define LL long long
const int maxn = 100000 + 100;
struct Node {
int pos, Index;
Node() {}
Node(int p, int I) {
pos = p;
Index = I;
}
};
struct Edge {
int u, v;
};
int n, m, u, v, cnt, mm, cnttmp, cas;
int fa[maxn], num[maxn], deg[maxn], ans[maxn << 2], anstmp[maxn];
vector<Node> G[maxn];
vector<int> GG[maxn];
Edge edge[maxn << 2];
int vis[maxn << 2];
bool cmp(const int &a, const int &b) {
return deg[a] > deg[b];
}
void Init() {
cnt = 0;
mm = m + 1;
for(int i = 1; i <= n; ++i) {
fa[i] = i;
num[i] = 0;
deg[i] = 0;
G[i].clear();
GG[i].clear();
}
}
int Find(int x) {
return (x == fa[x]? x: fa[x] = Find(fa[x]));
}
void unit(int x, int y) {
int xx = Find(x);
int yy = Find(y);
if(deg[xx] % 2 == 1) {
swap(xx, yy);
}
if(xx != yy) {
fa[xx] = yy;
num[yy] += num[xx];
}
}
void dfs(int x) {
int len = G[x].size();
for(int i = 0; i < len; ++i) {
int pos = G[x][i].pos;
int Index = G[x][i].Index;
if(vis[abs(Index)] != cas) {
vis[abs(Index)] = cas;
dfs(pos);
ans[cnt++] = Index;
}
}
}
int Next(int x, int mod) {
return ((x - 1) % mod + mod) % mod;
}
void solve(int pos) {
cnt = 0;
sort(GG[pos].begin(), GG[pos].end(), cmp);
int len = GG[pos].size();
for(int i = 0; i < len; i += 2) {
if(deg[GG[pos][i]] == 1) {
u = GG[pos][i];
v = GG[pos][i + 1];
G[u].push_back(Node(v, mm));
G[v].push_back(Node(u, -mm));
++mm;
} else {
break;
}
}
dfs(pos);
int End = -1;
for(int i = 0; i < cnt; ++i) {
if(abs(ans[i]) > m) {
End = i;
break;
}
}
if(End == -1) {
printf("%d", cnt);
for(int i = cnt - 1; i >= 0; --i) {
printf(" %d", ans[i]);
}
printf("\n");
return ;
}
for(int i = Next(End, cnt); i != End; ) {
cnttmp = 0;
bool flag = false;
for(int j = i; j != End; j = Next(j, cnt)) {
if(abs(ans[j]) > m) {
i = Next(j, cnt);
flag = true;
break;
}
anstmp[cnttmp++] = ans[j];
}
if(!flag) {
i = End;
}
if(cnttmp == 0) {
continue;
}
printf("%d", cnttmp);
for(int j = 0; j < cnttmp; ++j) {
printf(" %d", anstmp[j]);
}
printf("\n");
}
}
int main() {
#ifdef Dmaxiya
freopen("test.txt", "r", stdin);
#endif // Dmaxiya
ios::sync_with_stdio(false);
while(scanf("%d%d", &n, &m) != EOF) {
++cas;
Init();
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(Node(v, i));
G[v].push_back(Node(u, -i));
++deg[u];
++deg[v];
edge[i].u = u;
edge[i].v = v;
}
for(int i = 1; i <= n; ++i) {
deg[i] %= 2;
if(deg[i] == 1) {
++num[i];
}
}
for(int i = 1; i <= m; ++i) {
unit(edge[i].u, edge[i].v);
}
for(int i = 1; i <= n; ++i) {
GG[Find(i)].push_back(i);
}
for(int i = 1; i <= n; ++i) {
if((int)GG[i].size() <= 1) {
continue;
}
cnt += max(num[i] / 2, 1);
}
printf("%d\n", cnt);
for(int i = 1; i <= n; ++i) {
if((int)GG[i].size() <= 1) {
continue;
}
solve(i);
}
}
return 0;
}