E. Even Degree 【欧拉回路】

题目链接


题意
给一张图,当结点u,v的度不都是奇数时,我们可以删掉结点u,v之间的边。问:最多能删掉多少条边,并按照删除的顺序输出边的序号。题目不保证图是连通的,但是保证所有点的度在初始时都为偶数,也就是保证每个连通块都是一个欧拉回路。
思路
对于一个欧拉回路,我们先假装删除一条边让其变成一个欧拉通路,此时存在两个奇数度点。我们选择一个作为起点,一个作为终点。我们 dfs 从起点遍历到终点,在回溯时才往栈中存边,那么从栈底到栈顶就是正确的合法路径。问题是怎么找到这个路径呢? 在欧拉通路中删边时,我们总是可以保证只有两个奇数点。利用这个性质,我们跑整个欧拉回路,按照回溯存边的方法将所有的边推入栈。我们从栈底开始删边,如果合法则删除。 如果栈底的边的两个结点都是奇数点:其实这个时候只有一种情况并且只会出现一次,就是这条边有一个点是起点。如果这个时候未跑的边数大于1,那么起点一定还有一个边 x 连接着偶数点,这个时候我们可以删除边 x 以此来改变起点的奇偶性。所以这个时候我们可以删除栈顶的边,也就是将奇数点转移。(为什么栈顶的边是可以的?因为最后一条边一定是连接到起点的,跑欧拉回路最终要回到起点的嘛。)这样我们最后只会剩下一条边。

Code

#include <iostream>
#include <cstdio>
using namespace std;
int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
const int maxN = 500005;
int du[maxN];
bool vis[maxN];
int n, m;
struct EDGE{
    int adj, to, id;
    EDGE(int a = -1, int b = 0, int c = 0): adj(a), to(b), id(c) {}
}edge[maxN << 1];
int head[maxN], cnt;
void init() {
    cnt = 0;
    for(int i = 0; i <= n; ++ i ) {
        head[i] = -1;
        du[i] = 0;
    }
    for(int i = 0; i <= m; ++ i ) {
        vis[i] = false;
    }
}
void add_edge(int u, int v, int id) {
    edge[cnt] = EDGE(head[u], v, id);
    head[u] = cnt ++ ;
}
pair<int, int>pi[maxN];
int Stack[maxN], ans[maxN], top, tot;
void dfs(int u) {
    while(~head[u]) {
        int i = head[u];
        int v = edge[i].to;
        int id = edge[i].id;
        head[u] = edge[i].adj;
        if(vis[id]) continue;
        vis[id] = true;
        dfs(v);
        Stack[++top] = id;
    }
}
void solve() {
    int l = 1, r = top;
    while(l < r) {
        int u = pi[Stack[l]].first, v = pi[Stack[l]].second;
        if(du[u] & 1 && du[v] & 1) {
            ans[++ tot] = Stack[r];
            u = pi[Stack[r]].first, v = pi[Stack[r]].second;
            -- r;
        } else {
            ans[ ++ tot] = Stack[l];
            ++ l;
        }
        -- du[u], -- du[v];
    }
}
int main() {
    n = read(), m = read();
    init();
    for(int i = 1; i <= m; ++ i ) {
        int u, v;
        u = read(), v = read();
        add_edge(u, v, i);
        add_edge(v, u, i);
        pi[i] = make_pair(u, v);
        ++ du[u], ++ du[v];
    }
    for(int i = 1; i <= n; ++ i ) {
        top = 0;
        dfs(i);
        if(top) {
            solve();
        }
    }
    printf("%d\n", tot);
    for(int i = 1; i <= tot; ++ i ) {
        printf("%d%c", ans[i], (i == tot ? '\n' : ' '));
    }
    return 0;
}
/*
12 16
1 2
1 3
1 11
1 12
2 4
2 5
2 3
4 8
5 9
8 9
3 6
3 7
6 9
7 10
11 12
9 10
 ans:
15
1 5 8 10 9 6 7 11 13 16 14 12 4 2 3

14 19
1 2
1 3
1 11
1 12
2 4
2 5
2 3
4 8
5 9
8 9
3 6
3 7
6 9
7 10
11 12
9 10
1 13
1 14
13 14
 ans:
18
1 5 8 10 9 6 7 11 13 16 14 12 18 2 3 15 4 17
 */

栏目
728_90 cn stocks