2-sat + 二分 hdu3715

题目链接


http://acm.hdu.edu.cn/showproblem.php?pid=3715
 题意为有4个数组,其中a, b, c是给定数组且长度为m。a, b数组中的值在0~n - 1范围内,c数组的值在0~2范围内。有x数组,x数组仅包含0, 1。当x[a[dep]] + x[b[dep]]不等于c[dep]时且dep < m时,dep+=1问能够达到的最大dep是多少。
 可以看出x只能取两个状态所以就是2-sat。c为0时,a[dep]和b[dep]所代表的x不能同时为0;c为1时两者要么同时为0要么同时为1;c为2时,两者不能同时为1。建图后就是跑2-sat判断了。
 最开始我直接遍历1~m每次分别求2-sat这样建图可以记忆化但是T了。之后二分,枚举答案,每次重新建图判断是否满足条件即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
using namespace std;
struct Edge
{
    int v, next;
}edge[100004];
int head[405];
int cnt_Edge;
int cnt_Bcnt;
int Bcnt[405];
int low[405], dfn[405];
int Instack[405];
stack<int>SS;
int Index;
int n, m;
int a[10004], b[10004], c[10004];
void init()
{
    memset(head, -1, sizeof(head));
    cnt_Edge = 0;
}
void add_Edge(int u, int v)
{
    edge[cnt_Edge].v = v;
    edge[cnt_Edge].next = head[u];
    head[u] = cnt_Edge++;
}
void get_Map(int dep)
{
    rep(i, 0, dep - 1)
    {
        int lsa = a[i];
        int lsb = b[i];
        if(c[i] == 0)
            add_Edge(lsa<<1, lsb<<1^1), add_Edge(lsb<<1, lsa<<1^1);
        else if(c[i] == 1)
            add_Edge(lsa<<1^1, lsb<<1^1), add_Edge(lsb<<1^1, lsa<<1^1),
            add_Edge(lsa<<1, lsb<<1), add_Edge(lsb<<1, lsa<<1);
        else if(c[i] == 2)
            add_Edge(lsa<<1^1, lsb<<1), add_Edge(lsb<<1^1, lsa<<1);
    }
}
void tarjan(int a)
{
    low[a] = dfn[a] = ++Index;
    Instack[a] = 1;
    SS.push(a);
    for(int i = head[a];i != -1;i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[a] = min(low[a], low[v]);
        }
        else
        {
            if(Instack[v] == 1&&dfn[v] < low[a])
                low[a] = dfn[v];
        }
    }
    if(low[a] == dfn[a])
    {
        int ls;
        cnt_Bcnt++;
        do
        {
            ls = SS.top();
            SS.pop();
            Instack[ls] = 0;
            Bcnt[ls] = cnt_Bcnt;
        }
        while(ls != a);
    }
}
void find_Scc()
{
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(Bcnt, 0, sizeof(Bcnt));
    memset(Instack, 0, sizeof(Instack));
    cnt_Bcnt = 0;
    Index = 0;
    rep(i, 0, 2 * n - 1){
        if(!dfn[i])
            tarjan(i);
    }
}
int solve()
{
    rep(i, 0, n - 1)
    {
        if(Bcnt[i<<1] == Bcnt[i<<1^1])
            return 0;
    }
    return 1;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        rep(i, 0, m - 1)
            scanf("%d%d%d", &a[i], &b[i], &c[i]);
        int l = 1;
        int r = m;
        int mid;
        int ans = 0;
        while(r >= l)
        {
            mid = (l + r)>>1;
            init();
            get_Map(mid);
            find_Scc();
            if(solve())
            {
                l = mid + 1;
                ans = max(ans, mid);
            }
            else
                r = mid - 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值