codeforces CF19E Fairy 生成树 树上差分

$ \rightarrow $ 戳我进CF原题

E. Fairy


time limit per test: 1.5 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output


 

Once upon a time there lived a good fairy A.
One day a fine young man B came to her and asked to predict his future.
The fairy looked into her magic ball and
said that soon the fine young man will meet the most beautiful princess ever and will marry her.
Then she drew on a sheet of paper n points and joined some of them with segments,
each of the segments starts in some point and ends in some other point. Having drawn that picture,
she asked the young man to erase one of the segments from the sheet.
Then she tries to colour each point red or blue so, that there is no segment having points of the same colour as its ends.
If she manages to do so, the prediction will come true. B wants to meet the most beautiful princess,
that's why he asks you to help him. Find all the segments that will help him to meet the princess.
 

Input

The first input line contains two integer numbers:
$ n $ — amount of the drawn points and $ m $ — amount of the drawn segments $ (1 ≤ n ≤ 10^4, 0 ≤ m ≤ 10^4) $ .
The following m lines contain the descriptions of the segments.
Each description contains two different space-separated integer numbers
$ v, u (1 ≤ v ≤ n, 1 ≤ u ≤ n) $ — indexes of the points, joined by this segment.
No segment is met in the description twice.
 

Output

In the first line output number $ k $ — amount of the segments in the answer.
In the second line output $ k $ space-separated numbers — indexes of these segments in ascending order.
Each index should be output only once. Segments are numbered from 1 in the input order.
 

Examples

input1

 4 4
 1 2
 1 3
 2 4
 3 4

output1

 4
 1 2 3 4 

input2

 4 5
 1 2
 2 3
 3 4
 4 1
 1 3

output

 1
 5

 

题目大意

  • 给定一张无向图,要求删掉一条边,使得图变成二分图,问所有的删法。

  • $ n,m \le 10000 $
     

题解

  • 求出任意一棵生成树,并对这棵树进行二分图染色。

  • 考虑所有非树边,如果没有矛盾,说明本来就是二分图

    扫描二维码关注公众号,回复: 3083452 查看本文章
  • 如果有一条边 $ (x,y) $ 矛盾,可以删掉该边,或者树上从 $ x $ 到 $ y $ 路径上的任意一条边。

  • 如果有 $ >1 $ 条边矛盾,只能删掉“被所有矛盾边覆盖,不被非矛盾边覆盖”的树边。

  • 统计非树边对树边的覆盖次数即可。

  • 对于所有奇环,在非树边的 $ (u,v) $ 两端差分一个 $ 1 $ ,在 $ lca(u,v) $ 处 $ -2 $ ;

  • 对于其他偶环,在非树边的 $ (u,v) $ 两端差分一个 $ -1 $ ,在 $ lca(u,v) $ 处 $ +2 $ ;

  • 对于只有一个奇环的情况,奇环上的每条边都可以被删掉;

  • 对于有两个以上奇环的情况,要么无解,要么只能删掉树边(奇环套奇环),因为如果删掉矛盾的非树边,奇环情况不会改变。
     

代码

/**************************************************************
    Problem: 4424
    User: PotremZ
    Language: C++
    Result: Accepted
    Time:3404 ms
    Memory:205320 kb
****************************************************************/

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000010
struct edge{ int v,nxt,id; }G[maxn<<1];
struct Edge{ int u,v; bool frog; }e[maxn];
int head[maxn],tot,cnt,used;
void add(int u,int v,int id){
    G[++tot].v=v; G[tot].id=id; G[tot].nxt=head[u]; head[u]=tot;
    G[++tot].v=u; G[tot].id=id; G[tot].nxt=head[v]; head[v]=tot;
}
int n,m,fa[maxn],f[maxn][21],dep[maxn],bel[maxn],s[maxn],ans[maxn];
bool vis[maxn];
int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
void dfs(int u){
    vis[u]=1;
    for(int v,i=head[u];i;i=G[i].nxt)
        if(f[u][0]!=(v=G[i].v)){
            f[v][0]=u;
            dep[v]=dep[u]+1; bel[v]=i;
            for(int j=1;(1<<j)<=dep[v];++j)
                f[v][j]=f[f[v][j-1]][j-1];
            dfs(v);
        }
}
inline int lca(int u,int v){
    if(dep[u]>dep[v]) swap(u,v);
    for(int i=20;i>=0;--i)
        if(dep[u]<=dep[v]-(1<<i)) v=f[v][i];
    if(u==v) return u;
    for(int i=20;i>=0;--i)
        if(f[u][i]!=f[v][i]){ u=f[u][i]; v=f[v][i]; }
    return f[u][0];
}
void dfs2(int u){
    vis[u]=1;
    for(int i=head[u];i;i=G[i].nxt)
        if(f[u][0]!=G[i].v){ dfs2(G[i].v); s[u]+=s[G[i].v]; }
    //如果它满足不被覆盖,那么这条树边就可以被删除
    if(s[u]==cnt) ans[++ans[0]]=G[bel[u]].id;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i) fa[i]=i;
    for(int i=1;i<=m;++i){
        scanf("%d %d",&e[i].u,&e[i].v);
        int fu=find(e[i].u),fv=find(e[i].v);
        if(fu==fv) e[i].frog=1;
        else{ fa[fu]=fv; add(e[i].u,e[i].v,i); }
    }
    for(int i=1;i<=n;++i) if(!vis[i]) dfs(i);
    for(int i=1;i<=m;++i)
        if(e[i].frog){
            //进行差分,偶环和奇环共同占用的边不能被删掉
            int tmp=lca(e[i].u,e[i].v),tp=-1;
            if(!((dep[e[i].u]+dep[e[i].v])&1)){ tp=1; ++cnt; used=i; }
            s[e[i].u]+=tp; s[e[i].v]+=tp;
            s[tmp]-=2*tp;
        }
    //如果没有环,所有边都可以被删掉
    if(!cnt){
        printf("%d\n",m);
        for(int i=1;i<m;++i) printf("%d ",i); if(m) printf("%d",m);
        return 0;
    }
    memset(vis,0,sizeof(vis));
    //只有一个奇环,奇环上边都可以删
    if(cnt==1) ans[++ans[0]]=used;
    for(int i=1;i<=n;++i) if(!vis[i]) dfs2(i);
    sort(ans+1,ans+1+ans[0]);
    printf("%d\n",ans[0]);
    for(int i=1;i<ans[0];++i) printf("%d ",ans[i]); if(ans[0]) printf("%d",ans[ans[0]]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PotremZ/p/9610295.html