【tree】codeforces 19E.Fairy

http://codeforces.com/problemset/problem/19/E

      再次倒在WJMZBMR面前=。=,看了他的题解才懂这个题。。

      题意很简单,给你一个图,问你有哪些边删去之后(只删除一条)图将变成2部图。。。做法是构造出一棵生成树,树必定是2部图,将所有节点染色,之后根据染色结果将剩下的所有非树边分成两个集合,集合A中边连着两个不同颜色的点,集合B中边连着两个相同颜色的点。。

       先考虑非树边很容易根据集合B大小,分成等于0 , 等于1, 大于1三种情况,

       再考虑树边,我们将非树边的边的两个节点在树中的路径定义为该边的路径,则如果集合B中的某一条边的路径没有经过树边e,则删去e仍然存在一奇数环;同时如果集合A中某条边的路径以及集合B中某条边的路径同时经过树边e,则删出树边e仍存在奇数环。。除了这两种情况剩下的树边都是答案了,这两种情况也可以简单画一下便明白。。最后,可以用树分治,或者树剖分来统计树上边被路径覆盖次数,复杂度O(mlogn),最后附剖分版本程序

View Code
  1 //By Lin
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<vector>
  5 #include<algorithm>
  6 #define maxn 10010
  7 using namespace std;
  8 
  9 int        n,m;
 10 vector<int> ans;
 11 int        ecnt;
 12 struct    Edge{
 13     int to,num; 
 14     Edge *next;
 15 }*mat[maxn],edges[maxn*2];
 16 void    link(int x,int to,int num){
 17     edges[ecnt].to = to; 
 18     edges[ecnt].num= num;
 19     edges[ecnt].next = mat[x];
 20     mat[x] = &edges[ecnt++];
 21 }
 22 
 23 struct    Node{
 24     int x,y,num;
 25     Node(int _x,int _y,int _num):x(_x),y(_y),num(_num){}
 26 };
 27 vector<Node> data;
 28 int        p[maxn];
 29 int        Find(int x){
 30     return p[x]==x?x:(p[x]=Find(p[x])); 
 31 }
 32 
 33 int        size[maxn],son[maxn],top[maxn],dep[maxn],fa[maxn],cnt;
 34 int        id[maxn],in_tree[maxn],w[maxn],sum[2][maxn];
 35 bool    col[maxn];
 36 void    dfs1(int x,int father,bool cc){
 37     col[x] = cc;
 38     size[x] = 1; 
 39     son[x] = -1;
 40     for ( Edge *p = mat[x]; p ; p = p->next ){
 41         int to = p->to;
 42         if ( to == father ) continue;
 43         dep[to] = dep[x]+1;
 44         fa[to] = x;
 45         id[to] = p->num;
 46         dfs1( to , x, !cc );
 47         size[x] += size[to];
 48         if ( son[x] == -1 || size[to] > size[son[x]] ) son[x] = to;
 49     }
 50 }
 51 
 52 void    dfs2(int x,int father,int tp){
 53     top[x] = tp;
 54     w[ in_tree[x] = cnt++ ] = x;
 55     if ( son[x] == -1 ) return;
 56     dfs2( son[x] , x , tp );
 57     for ( Edge *p = mat[x]; p ; p = p->next ){
 58         int to = p->to;
 59         if ( to == father || to == son[x] ) continue;
 60         dfs2(to,x,to);
 61     }
 62 }
 63 
 64 int        main(){
 65     scanf("%d%d", &n, &m );
 66     for (int i = 1; i<=n; i++) p[i] = i;
 67     for (int i = 0; i<m; i++) {
 68         int x,y;
 69         scanf("%d%d", &x, &y );
 70         if ( Find(x) == Find(y) ) data.push_back( Node(x,y,i+1) );
 71         else{
 72             p[Find(x)] = Find(y);
 73             link( x , y , i+1 ) , link( y , x , i+1 );
 74         }
 75     }
 76     for (int i = 1; i<=n; i++) if ( p[i] == i ) link(0,i,0);
 77     dfs1( 0 , -1 , 1 );
 78     dfs2( 0 , -1 , 0 );
 79     int tol = 0;
 80     for (int i = 0; i<data.size(); i++) tol += col[data[i].x] == col[data[i].y];
 81     for (int i = 0; i<data.size(); i++){
 82         if ( tol == 0 || tol == 1 && col[data[i].x] == col[data[i].y] ) ans.push_back( data[i].num );
 83         int k = col[data[i].x] != col[data[i].y];
 84         int u = data[i].x , v = data[i].y;
 85         while ( top[u] != top[v] ) {
 86             if ( dep[top[u]] < dep[top[v]] ) swap(u,v);
 87             sum[k][in_tree[u]+1]--;
 88             sum[k][in_tree[top[u]]]++;
 89             u = fa[top[u]];
 90         }
 91         if ( dep[u] < dep[v] ) swap(u,v);
 92         sum[k][in_tree[v]+1]++;
 93         sum[k][in_tree[u]+1]--;
 94     }
 95     for (int i = 1; i<=n; i++) {
 96         sum[0][i] += sum[0][i-1];
 97         sum[1][i] += sum[1][i-1];
 98         if ( (tol == 0 || sum[0][i] == tol && sum[1][i] == 0) && id[w[i]] ) ans.push_back( id[w[i]] );
 99     }
100     sort( ans.begin() , ans.end() );
101     printf("%d\n" , (int)ans.size() );
102     for (int i = 0; i<ans.size(); i++) printf("%d%c" , ans[i] , i == ans.size()-1?'\n':' ' );
103     return 0;
104 }

 

转载于:https://www.cnblogs.com/lzqxh/archive/2012/12/27/2834990.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值