Description:
题解:
mdzz今天肝c两小时因为copy错了样例,最后被卡常,a没拍直接爆了,b还没时间想,其实是最简单的。
对于每一个连通块单独考虑。
一个联通块度数为奇数的点肯定是偶数个的,那么用欧拉路径去覆盖这个联通块最少就需要 度数为奇数的点个数2 度 数 为 奇 数 的 点 个 数 2
考虑如何找到这些路径?
建立超级源,向每个奇点连边,从这个点开始,跑欧拉回路,最后被这个点分隔开的就是那些路径了。
现在难点在于删边,这是个无向图,一条边被删的话,其对应的那条反向边也要删掉,这个不用真的删掉,打个标记,在以后用到那条边的时候直接跳过就行了,注意code里的打法。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define pb push_back
using namespace std;
const int N = 4e5 + 5;
int n, m, x, y, td, r[N], f[N], d[N], p[N];
int fi(int x) { return f[x] == x ? x : (f[x] = fi(f[x]));}
int final[N], to[N], next[N], tot = 1, bz[N];
void link(int x, int y) {
next[++ tot] = final[x], to[tot] = y, final[x] = tot;
}
struct edge {
int final[N], to[N], next[N], tot;
void link(int x, int y) { next[++ tot] = final[x], to[tot] = y, final[x] = tot;}
} e;
void dg(int x) {
for(int i = final[x]; i; i = final[x]) {
final[x] = next[i];
if(bz[i]) continue;
bz[i] = bz[i ^ 1] = 1;
dg(to[i]);
}
p[++ p[0]] = x;
}
vector<int> b[N]; int tb;
int main() {
freopen("miner.in", "r", stdin);
freopen("miner.out", "w", stdout);
scanf("%d %d", &n, &m);
fo(i, 1, n) f[i] = i;
fo(i, 1, m) {
scanf("%d %d", &x, &y);
r[x] ++; r[y] ++;
link(x, y); link(y, x);
if(fi(x) != fi(y)) f[f[x]] = f[y];
}
fo(i, 1, n) if(r[i]) e.link(fi(i), i);
td = n;
fo(i, 1, n) if(e.final[i]) {
d[0] = p[0] = 0;
for(int j = e.final[i]; j; j = e.next[j])
d[++ d[0]] = e.to[j];
td ++; int hh = 0;
fo(j, 1, d[0]) if(r[d[j]] & 1)
link(td, d[j]), link(d[j], td), hh ++;
if(hh) dg(td); else dg(i);
int la = 0; p[p[0] + 1] = n + 1;
fo(j, 1, p[0] + 1) if(p[j] > n) {
if(la < j - 1) {
b[++ tb].pb(j - la - 1);
fo(k, la + 1, j - 1) b[tb].pb(p[k]);
}
la = j;
}
}
printf("%d\n", tb - 1);
printf("%d\n", b[1][1]);
fo(i, 2, b[1][0]) printf("0 %d\n", b[1][i]);
fo(j, 2, tb) {
printf("1 %d\n", b[j][1]);
fo(i, 2, b[j][0]) printf("0 %d\n", b[j][i]);
}
}