[北京省选集训2019]图的难题 题解

传送门

题意:给一张无向图,将其中的边染成黑色或白色,使得不存在同色环。问是否存在可行的染色方案。

观察可知一个结论:图 G = ( V , E ) G=(V,E) 的可行的染色方案存在,当且仅当对于任意子图 G = ( V , E ) G'=(V',E') 都有 E 2 V 2 |E'|\leq2|V'|-2 ,即 E 2 V 2 |E'|-2|V'|\leq-2
如果我们定义一张子图的权值 Q ( G ) = E 2 V Q(G')=|E'|-2|V'| ,那么只要 Q ( G ) max 2 Q(G')_{\max}\leq-2 。比较显然的最大权闭合子图问题:把边看成点,点权为 1 1 ;点的点权为 2 -2 。那么我们从源点向代表边的点连边,容量是 1 1 ;从代表点的点向汇点连边,容量是 2 2 ;从边对应的点向其两个端点对应的点连边,容量是 + +\infty 表示割不掉;跑最大流,那么 Q max = E m a x f l o w Q_{\max}=|E|-\mathrm{maxflow}
但是这样会有问题:空子图的权值 Q ( ) = 0 > 2 Q(\empty)=0>-2 ,所以任何一张图都不合法。我们必须让我们的最大流算法不把空子图算进去。做法很简单,我们强制选一个点,那么从这个点对应的点到汇点的边就不连了,最后 Q max = E 2 m a x f l o w Q_{\max}=|E|-2-\mathrm{maxflow} 。那么强制选哪个点呢?枚举即可。

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>

template <typename T> inline void read(T& x) {
    int f = 0, c = getchar(); x = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
    if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
    read(x); read(args...); 
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }

const int maxv = 1607, maxe = 1e5, inf = INT_MAX;

int v[maxe << 1], cap[maxe << 1], flow[maxe << 1], next[maxe << 1], tot = -1;
int head[maxv], curr[maxv], q[maxv], height[maxv];
int s, t, V;

inline void ae(int x, int y, int c) {
    v[++tot] = y; cap[tot] = c; flow[tot] = 0; next[tot] = head[x]; head[x] = tot;
    v[++tot] = x; cap[tot] = 0; flow[tot] = 0; next[tot] = head[y]; head[y] = tot;
}
inline bool bfs() {
    for (int i = 1; i <= V; ++i) height[i] = 0;
    int l = 0, r = 1; height[q[1] = s] = 1;
    while (l < r) {
        int x = q[++l];
        for (int i = head[x]; ~i; i = next[i])
            if (cap[i] > flow[i] && !height[v[i]])
                height[q[++r] = v[i]] = height[x] + 1;
    }
    return height[t];
}
int dfs(int x, int cf) {
    if (x == t || !cf) return cf;
    int getf = 0;
    for (int i = curr[x]; ~i; i = next[i])
        if (cap[i] > flow[i] && height[v[i]] == height[x] + 1) {
            int nf = dfs(v[i], std::min(cf, cap[i] - flow[i]));
            if (nf) {
                flow[i] += nf; flow[i ^ 1] -= nf; getf += nf;
                if (!(cf -= nf)) break;
            }
        }
    return getf;
}
inline int maxflow() {
    int ans = 0;
    while (bfs()) {
        for (int i = 1; i <= V; ++i)
            curr[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

struct Edge {
    int u, v;
    Edge(int x, int y) : u(x), v(y) {}
    Edge() : u(0), v(0) {}
};
Edge e[1007];
int T, n, m;

inline bool solve() {
    int ans = -inf;
    s = n + m + 1;
    t = V = s + 1;
    for (int u = 1; u <= n; ++u) {
        tot = -1;
        for (int i = 1; i <= V; i += 4) {
            head[i] = -1;
            head[i + 1] = -1;
            head[i + 2] = -1;
            head[i + 3] = -1;
        }
        for (int i = 1; i <= m; ++i) {
            int x = e[i].u, y = e[i].v;
            ae(s, i, 1);
            ae(i, x + m, inf);
            ae(i, y + m, inf);
        }
        for (int i = 1; i <= n; ++i)
            if (i != u) ae(i + m, t, 2);
        chkmax(ans, m - 2 - maxflow());
    }
    return ans <= -2;
}

int main() {
    read(T);
    while (T--) {
        read(n, m);
        for (int i = 1; i <= m; ++i) read(e[i].u, e[i].v);
        puts(solve() ? "Yes" : "No");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39677783/article/details/89608850