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

传送门

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

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

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值