Codeforces Round #347 (Div. 2) D. Graph Coloring(强联通分量缩点+2-SAT)

题意:

给你n个点m条边,每条边有一种颜色(BR),每选择一个点可以使与其相连的所有边的颜色翻转,问最少需要选择多少个点,使得所有边的颜色相同,输出操作数以及操作哪些点。

(1 ≤ n,m ≤ 100000)


思路:

我们可以分别对把所有边变成BR分别考虑。

因为每个点不会操作2次或者更多,因为操作两次相当于不操作,操作三次相当于操作一次。

然后用2-sat强连通模板跑

假设现在要把所有边变成R,现在有u,v两点之间的边为B,

我们用v+n,u+n表示这两个点不翻转,u,v表示这两个点进行翻转

则我们可以建立以下四条边

u->v+n,v+n->u,u+n->v,v->u+n

由于是有向图,所以tarjan不需要判断父亲

</pre><pre name="code" class="cpp">#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-12
#define maxn 200100
#define MOD 1000000007

struct Edge
{
    int to,next;
} edge[maxn<<1];

int n,m,ans;
int tot,head[maxn],scc;
int dfn[maxn],low[maxn],time;
int sta[maxn],top,instack[maxn],vis[maxn];
int belong[maxn],num[maxn],flag[maxn];
vector<pair<int,int> > mp[maxn];
vector<int> tmp[maxn];

void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
void add_edge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++time;
    sta[++top] = u;
    instack[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instack[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        num[++scc] = 0;
        tmp[scc].clear();
        while(1)
        {
            int v = sta[top--];
            instack[v] = 0;
            belong[v] = scc;
            if(v <= n)
                num[scc]++;
            tmp[scc].push_back(v);
            if(v == u)
                break;
        }
    }
}

void solve(int key)
{
    init();
    for(int u = 1; u <= n; u++)
    {
        for(int i = 0; i < mp[u].size(); i++)
        {
            int v = mp[u][i].first;
            int c = mp[u][i].second;
            if(c == key)
            {
                add_edge(u,v);
                add_edge(u+n,v+n);
            }
            else
            {
                add_edge(u,v+n);
                add_edge(u+n,v);
            }
        }
    }
    top = time = scc = 0;
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    memset(flag,0,sizeof(flag));
    for(int i = 1; i <= 2*n; i++)
        if(!dfn[i])
            tarjan(i);
    for(int i = 1; i <= n; i++)
        if(belong[i] == belong[i+n])
            return;
    int cnt = 0;
    for(int i = 1; i <= n; i++)
    {
        if(flag[i])
            continue;
        int k;
        if(num[belong[i]] <= num[belong[i+n]])
            k = i;
        else
            k = i + n;
        cnt += num[belong[k]];
        for(int j = 0; j < tmp[belong[k]].size(); j++)
        {
            int v = tmp[belong[k]][j];
            flag[v] = 1;
            flag[v<=n?v+n:v-n] = 2;
        }
    }
    if(cnt < ans)
    {
        memset(vis,0,sizeof(vis));
        ans = cnt;
        for(int i = 1; i <= n; i++)
            if(flag[i] == 1)
                vis[i] = 1;
    }
}
int main()
{
    int t;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        for(int i = 1; i <= n; i++)
            mp[i].clear();
        for(int i = 0; i < m; i++)
        {
            int u,v;
            char c;
            scanf("%d%d %c",&u,&v,&c);
            mp[u].push_back(make_pair(v,c=='B'?1:2));
            mp[v].push_back(make_pair(u,c=='B'?1:2));
        }
        ans = INF;
        solve(1);
        solve(2);
        if(ans == INF)
            printf("-1\n");
        else
        {
            printf("%d\n",ans);
            for(int i = 1; i <= n; i++)
                if(vis[i])
                    printf("%d ",i);
            printf("\n");
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值