[杂题]Codeforces 663C. Graph Coloring

Description

就是图上的开关灯游戏。
每条边有一个颜色,每次选择一个点可以翻转一种颜色。
求使所有颜色相同的最小操作数。

Solution

枚举最后染成的颜色。
如果一条边颜色与最后染成的颜色相同,那么它连接的两个点必须同时选或者同时不选。
那么只要dfs一次就好了。
有一个小坑(不过也是我太菜了啊QAQ)就是联通块要分开来算。。

#include <bits/stdc++.h>
using namespace std;

const int N = 101010;
const int INF = 1 << 30;
typedef long long ll;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline char Get(void) {
    static char c;
    for (c = get(); c != 'R' && c != 'B'; c = get());
    return c;
}

struct edge {
    int to, next, key;
    edge(int t = 0, int n = 0, int k = 0):to(t), next(n), key(k) {}
};
edge G[N << 1];
int head[N];
int n, m, Gcnt, x, y, flag, f1, f2, cnt, Ans, clc, bl;
int col[N], ans[N], vis[N], tmp[N], ans1[N];

inline void AddEdge(int from, int to, int key) {
    G[++Gcnt] = edge(to, head[from], key); head[from] = Gcnt;
    G[++Gcnt] = edge(from, head[to], key); head[to] = Gcnt;
}
inline void dfs(int u, int f) {
    cnt += f; col[u] = f; ++bl;
    for (int i = head[u]; i; i = G[i].next) {
        if (col[G[i].to] == -1) dfs(G[i].to, f ^ G[i].key);
        else if (col[G[i].to] ^ f ^ G[i].key) flag = 1;
    }
}
inline void dfs1(int u, int v) {
    vis[u] = clc; ans[u] = col[u] ^ v;
    for (int i = head[u]; i; i = G[i].next)
        if (vis[G[i].to] != clc) dfs1(G[i].to, v);
}

int main(void) {
    read(n); read(m);
    for (int i = 0; i < m; i++) {
        read(x); read(y);
        if (Get() == 'R') AddEdge(x, y, 0);
        else AddEdge(x, y, 1);
    }
    Ans = INF;
    for (int i = 1; i <= n; i++) tmp[i] = INF;
    for (int i = 1; i <= n; i++) col[i] = -1;
    f1 = f2 = 0;
    for (int i = 1; i <= n; i++)
        if (col[i] == -1) {
            bl = flag = cnt = 0; dfs(i, 0);
            f1 |= flag;
            if (flag) break;
            if (cnt < tmp[i]) {
                ++clc; dfs1(i, 0);
                tmp[i] = cnt;
            }
            if (bl - cnt < tmp[i]) {
                ++clc; dfs1(i, 1);
                tmp[i] = bl - cnt;
            }
        }
    if (!f1) {
        Ans = 0;
        for (int i = 1; i <= n; i++) if (ans[i]) Ans++;
        for (int i = 1; i <= n; i++) ans1[i] = ans[i];
    }
    for (int i = 1; i <= Gcnt; i++) G[i].key ^= 1;
    for (int i = 1; i <= n; i++) tmp[i] = INF;
    for (int i = 1; i <= n; i++) col[i] = -1;
    for (int i = 1; i <= n; i++)
        if (col[i] == -1) {
            bl = flag = cnt = 0; dfs(i, 0);
            f2 |= flag;
            if (flag) break;
            if (cnt < tmp[i]) {
                ++clc; dfs1(i, 0);
                tmp[i] = cnt;
            }
            if (bl - cnt < tmp[i]) {
                ++clc; dfs1(i, 1);
                tmp[i] = bl - cnt;
            }
        }
    if (f1 && f2) Ans = -1;
    else {
        cnt = 0;
        for (int i = 1; i <= n; i++) if (ans[i]) cnt++;
        if (cnt > Ans) for (int i = 1; i <= n; i++) ans[i] = ans1[i];
        else Ans = cnt;
    }
    printf("%d\n", Ans);
    if (Ans > 0)
        for (int i = 1; i <= n; i++)
            if (ans[i]) printf("%d ", i);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值