【代码】POJ 3678

// 题目来源:POJ 3678
// 题目大意:有很多点可取值0或1,它们之间有很多边为0或1,规定每条边由端点(xor,and,or)得到,求是否可行
// 解决方法:每个点的取值只有两种情况,非常符合2-sat,用强连通分量判断即可
// 特别注意:可通过在拆成的点对中搭边来保证最后的结果落在某个点上

#include <cstdio>
#include <string>
#define o 1002
#define _ 1000002

void link( int, int );
void tarjan( int );

int next[ _*4 ], g[ _*4 ], h[ o*4 ], code[ o*4 ], dfn[ o*4 ], low[ o*4 ], stack[ o*4 ];
int n, m, index, cnt, t, top;
bool ins[ o*4 ];

int main( )
{
    freopen( "3678.in", "r", stdin );
    freopen( "3678.out", "w", stdout );
    scanf( "%d%d", &n, &m );
    int aa, bb, cc;
    char st[ 10 ];
    for( int i = 1; i <= m; i++ )
    {
        scanf( "%d%d%d%s", &aa, &bb, &cc, st );
        if( strcmp( st, "AND" ) == 0 )
            if( cc == 0 )
            {
                link( aa+n, bb );
                link( bb+n, aa );
            }
            else
            {
                link( aa+n, bb+n );
                link( bb+n, aa+n );
                link( aa, aa+n );
                link( bb, bb+n );
            }
        if( strcmp( st, "OR" ) == 0 )
            if( cc == 0 )
            {
                link( aa, bb );
                link( bb, aa );
                link( aa+n, aa );
                link( bb+n, bb );
            }
            else
            {
                link( aa, bb+n );
                link( bb, aa+n );
            }
        if( strcmp( st, "XOR" ) == 0 )
            if( cc == 0 )
            {
                link( aa, bb );
                link( bb, aa );
                link( aa+n, bb+n );
                link( bb+n, aa+n );
            }
            else
            {
                link( aa, bb+n );
                link( bb, aa+n );
                link( aa+n, bb );
                link( bb+n, aa );
            }
    }
    for( int i = 0; i < 2*n; i++ )
        if( !dfn[ i ] ) tarjan( i );
    for( int i = 0; i < n; i++ )
        if( code[ i ] == code[ i + n ] )
        {
            printf( "NO" );
            return 0;
        }
    printf( "YES" );
    return 0;
}

void link( int aa, int bb )
{
    next[ ++t ] = h[ aa ];
    h[ aa ] = t;
    g[ t ] = bb;
}

void tarjan( int i )
{
    int j;
    dfn[ i ] = low[ i ] = ++index;
    stack[ ++top ] = i;
    ins[ i ] = 1;
    for( int k = h[ i ]; k; k = next[ k ] )
    {
        j = g[ k ];
        if( !dfn[ j ] )
        {
            tarjan( j );
            if( low[ j ] < low[ i ] ) low[ i ] = low[ j ];
        }
        else if( ins[ j ] && dfn[ j ] < low[ i ] )
            low[ i ] = dfn[ j ];
    }
    if( dfn[ i ] == low[ i ] )
    {
        cnt++;
        do
        {
            j = stack[ top-- ];
            code[ j ] = cnt;
            ins[ j ] = 0;
        }
        while( i != j );
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值