嘘~ 正在从服务器偷取页面 . . .

CF19E Fairy 题解


CF19E Fairy 题解

题目链接:CF19E Fairy

题意

给定 \(n\) 个点,\(m\) 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。

输入格式

第一行包含两个正整数 \(n,m~(n,m\le10^6)\),分别表示点数和边数。

\(2\sim m+1\) 行,每行两个正整数 \(x,y~(1 \le x,y \le n,~x\ne y)\),表示有一条 \((x,y)\) 的边。

输出格式

第一行输出一个整数,表示能删除的边的个数。

第二行按照从小到大的顺序输出能删除的边的序号。

首先,一个图是二分图当且仅当图中没有奇数长度的环,即没有奇环。

那么我们只需要找到一些边,使得整个图删掉这些边以后没有奇环即可。

考虑建出原图的 DFS 树,则每个奇环对应了一条或多条非树边。

考虑只有一条非树边的奇环,不妨称之为主奇环。

结论1 若 \(e\) 为非树边,则 \(e\) 能被删除的当且仅当它位于(且仅位于)一个主奇环上。

证明 必要性显然,考虑证明充分性。

根据假设,若删除后还有奇环,则一定不是主奇环。

而任何一个非主奇环在 DFS 树上都能对应到一段链,从而至少对应到另一条非树边 \(e'\)

即在删除 \(e\) 之前,\(e'\) 也在一个主奇环上。由假设可知 \(e=e'\) ,故不可能存在非主奇环。\(\square\)

结论2 若 \(e\) 为树边,则 \(e\) 能被删除的当且仅当它在所有的主奇环上,且不在任何一个主偶环上。

证明 必要性显然,考虑证明充分性。

若删除后还有奇环,则

  • 若这个奇环对应的链不包含 \(e\) ,那么可以找到一个不经过 \(e\) 的主奇环,矛盾。
  • 若这个奇环对应的链包含 \(e\) ,那么可以找到经过 \(e\) 的主偶环,也矛盾。\(\square\)

那么我们需要统计的是,对于每条树边,有多少个主奇环/主偶环经过它。

由于每条主奇环/主偶环均对应一条非树边 \(e=(u,v)\) ,而 \(u\leadsto v\) 的每个节点都会得到 \(1\) 的贡献。

于是就变成了一个链上区间加,全局查询的问题了。考虑差分即可。

时间复杂度 \(\mathcal{O}(n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(1e6 + 15))
#define M ((int)(2e6 + 15))

bool tree_edge[M], vis[N];
int n, m, pos = 1, fa[N], dep[N], head[N];
int odd_cnt, ans_cnt, ans[N], odd[N], even[N], eodd[M], eeven[M];
struct Edge { int v, next; } e[M];
void addEdge(int u, int v) {  e[++pos] = {v, head[u]}; head[u] = pos; }
void dfs(int u)
{
    for(int i = head[u]; i; i = e[i].next)
    {
        const int &v = e[i].v;
        if(!~fa[v])
        {
            fa[v] = u; dep[v] = dep[u] + 1;
            tree_edge[i] = tree_edge[i ^ 1] = true; dfs(v); 
        }
    }
}
void dfs2(int u)
{
    vis[u] = true;
    for(int i = head[u]; i; i = e[i].next)
    {
        const int &v = e[i].v;
        if(fa[v] == u)
        {
            dfs2(v); odd[u] += odd[v]; even[u] += even[v];
            eodd[i] = eodd[i ^ 1] = odd[v];
            eeven[i] = eeven[i ^ 1] = even[v];
        }
    }
}
void output()
{
    cout << ans_cnt << '\n';
    rep(i, 1, ans_cnt) cout << ans[i] << " \n"[i == ans_cnt];
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m;
    rep(i, 1, m)
    {
        static int u, v; cin >> u >> v;
        addEdge(u, v); addEdge(v, u);
    }
    memset(fa, -1, sizeof(fa));
    rep(i, 1, n) if(!~fa[i]) { fa[i] = 0; dfs(i); }
    for(int i = 2; i <= pos; i += 2)
    {
        if(!tree_edge[i])
        {
            int u = e[i].v, v = e[i + 1].v;
            if(dep[u] < dep[v]) swap(u, v);
            if((dep[u] ^ dep[v]) & 1) { ++even[u], --even[v]; }
            else { ++odd[u], --odd[v], ++odd_cnt, eodd[i] = eodd[i ^ 1] = true; }
        }
    }
    if(!odd_cnt) { rep(i, 1, m) ans[++ans_cnt] = i; output(); return 0; }
    rep(i, 1, n) if(!vis[i]) dfs2(i);
    for(int i = 2; i <= pos; i += 2)
    {
        if(tree_edge[i]) {
            if(eodd[i] == odd_cnt && !eeven[i]) ans[++ans_cnt] = (i + 1) / 2;
        } else {
            if(eodd[i] && odd_cnt == 1) ans[++ans_cnt] = (i + 1) / 2;
        }
    }
    return output(), 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/records/lydsy4424%3Bcf19E.html


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录