Bzoj 2443: [Usaco2011 Open]奇数度数

2443: [Usaco2011 Open]奇数度数

>原题链接<

Description

奶牛们遭到了进攻!在他们的共和国里,有N(1 <= N <=50,000)个城市,由M(1 <= M <= 100,000)条无向的道路连
接城市A_i和B_i(1 <= A_i <= N;1 <= B_i <= N;A_i != B_i; 不会有重复的道路出现)。然而,整个共和国不一定
是连通的——有一些城市无法到达另外一些城市。入侵者想得到共和国的地图。(入侵者很傻,因此,他们的绘制
地图的方法是去访问每一条边,T_T)。奶牛想要折磨一下入侵者,使得他们尽可能难地完成地图绘制。因此,奶牛
会破坏若干条道路。请你帮助奶牛找到一个道路的子集,使得每条边每个点的度数为奇数。或者输出不存在这样的
一个子集。(奶牛的图论学得真好.= =||)举个例子,考虑下面的共和国:
1---2
\ /
  3---4
如果我们保留道路1-3,2-3和3-4,破坏道路1-2,那么城市1,2,4都只有一条边相连,城市3有3条边相连:
1   2
\ /
  3---4

Input

* 第一行:两个用空格隔开的整数:N和M
* 第二行到M+1行:第i+1行有两个空格隔开的整数A_i和

Output

* 第一行: 一个整数表示需要保留的道路数量
* 第二到K+1行:每行一个数表示保留的道路的编号,范围是1...M。

Sample Input

4 4
1 2
2 3
3 1
3 4

Sample Output

3
2
3
4

思路:

  一开始并没有什么好思路,和suika讨论了一下发现可以拽一颗DFS树出来进行树形DP。因为是无向图,所以保证DFS树不会出现横叉边

所以DP思路就很显然了,首先叶子结点的边不能砍掉。对于每个非叶子结点,如果它儿子带来的边的个数为奇数,那就需要把它和它父亲之间

的边砍掉。否则不能砍。最后在DFS树上留下的边就是答案需要的边。这样的话如果可以找到一个解。正确性是显然的。可是怎么证明这样找

不到解就一定没有解呢。那...我就不知道了。。。

// luogu-judger-enable-o2
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 51000, M = 210000;
int head[N], to[M] ,nxt[M], cnt=1, idx[M];
int dfn[N], rt[N], tot;
bool dont[M];
bool vis[M];
void add(int a,int b,int c) {
    to[++cnt] = b;
    nxt[cnt]  = head[a];
    head[a] = cnt;
    idx[cnt] = c;
    
    to[++cnt] = a;
    nxt[cnt]  = head[b];
    head[b] = cnt;
    idx[cnt] = c;
}
bool dfs(int p, int f) {
    int siz=0;
    dfn[p]=dfn[f]+1;
    for(int i=head[p];i;i=nxt[i]) {
        if(to[i]!=f&&!dfn[to[i]]) {
            if(dfs(to[i], p))
                dont[idx[i]] = 1;
            else {
                siz++;
                vis[idx[i]] = 1;
            }
        }
    }
    return siz%2;
}
int main() {
    int n, m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        int a, b;
        scanf("%d%d",&a,&b);
        add(a, b, i);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])    
            if(!dfs(i, 0)) {
                puts("-1");return 0;
            }
    int ans=0;
    for(int i=1;i<=m;i++) {
        if(!dont[i]&&vis[i]) ans++;
    } 
    printf("%d\n",ans);
    for(int i=1;i<=m;i++) {
        if(!dont[i]&&vis[i]) printf("%d\n",i);
    } 
}

 

posted @ 2018-06-14 19:36  TOBICHI_ORIGAMI  阅读(108)  评论(1编辑  收藏  举报