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
1 2
2 3
3 1
3 4
Sample Output
3
2
3
4
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); } }