【gdgzezoi】課題A:妖精

説明

図M(ループフリー)に縁なしに、n個の点を考えると、あなたは、エッジ除去は、図1の二部グラフにQを可能図形の一方の側から除去することができます。

入力

ライン1備える二つの整数N、M、それぞれ、点とエッジ。

2〜M + 1本のライン毎二つの数X、Yは、点X、Yを連結するエッジがあることを示します。

出力

エッジの数を表す2つの整数の最初の行は削除することができます。

出力側の昇順で次の行には、番号を削除することができます。

サンプル入力

4 4

1 2

1 3

2 4

3 4

サンプル出力

4

1 2 3 4

ヒント

データの10%、N、M <= 10

40データの%、N、M <= 1000

70データの%、N、M <= 100000

100%のデータ、nは、M <= 2000000

思考

まず、この図の図二部グラフの必要十分条件は、リングの不在が奇数である
:議論するために、3つのケースに分けることができる
①:図環が奇数でない、軽く削除条件が満たされています。
②:図奇数環、次いで辺を削除は、すべての奇数の環の共通の側で満たさなければなりません。
だから今、私たちはこのマップ上のDFSツリーを構築します。

簡単には数字が点からも、彼の先祖の端に木の側に加えて、我々は先祖返り側(図の赤側)として、それを覚えているだろうしていることを確認します。

ここに画像を挿入説明

反転エッジ(a、b)は、ツリー経路(B、A)は、図中の環を形成してもよいことは明らかで比較。
図3→1→2つの→3の形態奇数、4→1→2→3環 →4は、 結合リングによって形成されています。
アイデアので、この問題は、まず、すべてのリング前駆バックの唯一の奇妙な側面が含まれている番号を見つけ、比較的明確です。
単一の先祖リングとして記録リング先祖返りの片側だけを含めたいことがあり、そこにあることは明らかです。

①:単一図形ズッキリングあれば、任意のエッジ条件が満たされたグラフを削除します。
②:単一の図ズッキリングがある場合、側面を削除する満たす必要があり、すべての一般的な側面ズッキ単一のリングです。
今だけズッキリング一桁の場合について議論
削除先祖返り側が条件を満たすことができるが
、単一のズッキリングは、その後、ズッキ先祖返りシングルループエッジが一定の条件を満たして削除した場合:①。
②:複数の単一ズッキ環場合、任意のエッジを削除する条件を満たすように時代遅れではありません。(コモン側隔世遺伝側ズッキすべてではない単環)
の木の側が条件を満たすことができます削除します

この時点で、唯一の木の側がすべて単環住基によってを通して、次いでIF(A、B)は、反転側鎖ツリー(B、A)であるかどうかを検討環上にあります。

単一の祖先リングせずに木のいずれかの側で、すべての単環で、このエッジを削除する場合住基条件が満たされます。
なぜさえリングはそれに依存しますか?

ここに画像を挿入説明

リングが奇数である→1→2→5は、リングのみ奇数の先祖返りエッジ(5,1)を含有することが明らかです。
しかし、これは、図では省略されている。(2,5)または(1,5)という奇数図環ようなものでなくてもよいです。
(もし削除(1,2)、または奇数図リングの存在6→1→5→2→ 4→6、 リングは2つの奇数先祖返りの側面が含まれている)
我々は唯一考える前に、あなたが発見したかもしれません奇数の先祖返りリングの片側のみを含有し、環は、実際には2つの奇数先祖返りエッジを含んでもよいです。
環X住基単一祖先カップリングYにおける単一ターンのエッジ(a、b)があれば、偶数エッジを、それを削除し、XおよびYは依然として特異環を形成することができます。
削除ツリーは、条件は、単一の祖先リングを決定することも必要であるか否かが判定されるエッジ。
我々はDP奇数および偶数リングについて尋ねるよう環は次のように
点の親としてF [I]を配置私は、エッジの数は、単環住基、G [i]はiは単一の祖先の偶数である親エッジ点である含んでリングが含まれています。
ボトムアップのツリーDFS DPで。
エッジに対して、点Uを考える(u、v)は、3例がある:
①:(u、v)は木の側で、点Uは、V父[U] + = F、Fである [V]、G [U] + G = [V]
②:(U、V)は、反転エッジで、F [U] ++またはG [U] ++;
③:(V、U)が反転エッジで、F [U] - -又はG [U] - ; -
時間計算:O(N + M)

コード

#include<bits/stdc++.h>
using namespace std;
const int M=4e6+77;
typedef int arr[M];
arr to,g,nx,val,dfn,dep,fa;
int f[2][M],ans[M],tot[2][M];
int df,cnt,sum,n,m,x[M],y[M];
void add(int u,int v,int w)
{
    to[++cnt]=g[u]; nx[cnt]=v; g[u]=cnt; val[cnt]=w;
}
void dfs(int u,int ff)
{
    dfn[u]=++df;
    for(int i=g[u];i;i=to[i])
    {
        int v=nx[i];int w=val[i];
        if(w==fa[u])continue;
        if(dfn[v])
        {
            if(dep[v]==dep[u])
            {
                if(dfn[u]<dfn[v]) f[1][u]--;
                else
                {
                    ++sum; f[1][u]++; tot[1][w]++;
                }
            }
            else
            {
                if(dfn[u]<dfn[v]) f[0][u]--;else
                {
                    f[0][u]++;
                    tot[0][w]++;
                }
            }
            continue;
        }   
        fa[v]=w;
        dep[v]=dep[u]^1;
        dfs(v,u);
        for(int j=0;j<2;++j)
        {
            tot[j][w]+=f[j][v];
            f[j][u]+=f[j][v];
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;++i)
    {
        scanf("%d%d",&x[i],&y[i]);
        add(x[i],y[i],i);
        add(y[i],x[i],i);
    }
    for(int i=1;i<=n;++i)
    {
        if(!dfn[i])
        {
            dep[i]=1;
            dfs(i,i);
        }
    }
    if(!sum)
    {
        printf("%d\n",m);
        for(int i=1;i<=m;++i)printf("%d ",i);
        return 0;
    }
    memset(ans,0,sizeof ans);
    for(int i=0;i<m;++i)
    {
        if(!tot[0][i]&&tot[1][i]==sum)ans[++ans[0]]=i;
    }
    printf("%d\n",ans[0]);
    for(int i=1;i<=ans[0];++i)printf("%d ",ans[i]+1);
    return 0;
}
703元記事公開 ウォンの賞賛392 ビューに14万+を

おすすめ

転載: blog.csdn.net/Eric1561759334/article/details/100552484