1. 程式人生 > >codeforces CF19E Fairy 生成樹 樹上差分

codeforces CF19E Fairy 生成樹 樹上差分

lang 要求 test why 無向圖 給定 pri spa str

$ \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 $
     

題解

  • 求出任意一棵生成樹,並對這棵樹進行二分圖染色。

  • 考慮所有非樹邊,如果沒有矛盾,說明本來就是二分圖

  • 如果有一條邊 $ (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;
}

codeforces CF19E Fairy 生成樹 樹上差分