1511 与或式

51nod 1511 与或式
题目来源: CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题 收藏 关注
现在有一个与或式形式如下:

(v11|v12|…|v1k1) & (v21|v22|…|v2k2) & … & (vl1|vl2|…|vlkl)
&表示逻辑与,|表示逻辑或,vij是一些布尔变量或者是他们的取反。每一个括号内的表达式是一个块。

现在给定一个与或式和一些变量 x1,x2,…,xm 和他们的取反。每一个变量最多会出现在两个块中(变量本身和变量取反,比如 x1,¬x1 )。你的任务是判断一下是否有一组x的值使得这个与或式为真值。

样例解释:这个表达式是 (x1 | ¬x2)&(¬x1 | x2) 。其中一种解是 x1 = TRUE, x2 = TRUE。

Input
单组测试数据。
第一行包含两个整数 n 和m (1≤n,m≤2*10^5),表示块的个数和变量的个数。

接下来n行描述每一个块。
第i行先给出一个ki(ki≥1),表示第i个块中变量的个数。后面给出vij (1≤|vij|≤m),表示对应位置的变量是x[|vij|] ,如果vij是负的,就是表示取反,否则就是不取反。

Output
如果可以找到一组值,输出YES,否则输出NO。

Input示例

2 2
2 1 -2
2 2 -1

Output示例

YES

算法思想:
(v11|v12|…|v1k1) & (v21|v22|…|v2k2) & … & (vl1|vl2|…|vlkl)
只有各个块都为真时,整个表达式才为真。
因为一个变量只会出现两次,所以情况有限
1、只出现一次,那么那个表达式就能取1
2、一正一反,出现在一个表达式,那么这个表达式取1
3、一正一反,出现在两个不同的表达式中,不能确定表达式取值情况
4、两正或两反,变量所在的表达式肯定能取真值

根据上文分析,只有第三种情况是不能确定表达式取值情况的
对于n个表达式,n-1个变量在以只存在情况3,那么是整个表达式取不了真值的
x为e1,e2两个表达式共有的,一个表达式取真值,势必另一个表达式要取真值就不能依靠x来取得真值,
所以取得真值只能依靠其他变量,但是变量有限,所以不能所有表达式都取真值

所以,可以确定,n个不确定能否去真值的表达式,起码需要n个变量才能使所有表达式取值为真
所以,每两个表达式通过情况三产生关系时,就通过并查集联系起来,这样的话
一个集合中元素某一个确定可以取真值时,其他就可以取真值。
一个集合中两个元素还有情况三的变量时,集合中所有元素就可以取真值。
这次的算法思想写的应该是有史以来最详细的一次了-。-

#include<cstdio>
const int N=200005;
bool v[N];
int f[N],fi[N],se[N];
int read()
{
    int s=0,f=1;
    char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')f=-1,c=getchar();
    while(c>='0'&&c<='9')s=s*10+c-'0',c=getchar();
    return f*s;
}

int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
bool ok(int n)
{
    int i,x;
    for(i=1;i<=n;i++)
    {
        x=find(i);
        if(!v[x])
        {
            return 0;
        }
    }
    return 1;
}
int main()
{
    int i,j,k,x,y,n,m;
    n=read(),m=read();
    for(i=1;i<=n;i++)
        f[i]=i;
    for(i=1;i<=n;i++)
    {
        k=read();
        for(j=0;j<k;j++)
        {
            x=read();
            if(x>0)
            {
                if(!fi[x])fi[x]=i;
                else se[x]=i;
            }
            else
            {
                if(!fi[-x])fi[-x]=-i;
                else se[-x]=-i;
            }
        }
    }
    for(i=1;i<=m;i++)
    {
        if(!fi[i])continue;
        if(se[i])
        {
            if((fi[i]>0)==(se[i]>0))
            {
                if(fi[i]<0)fi[i]=-fi[i];
                if(se[i]<0)se[i]=-se[i];
                x=find(fi[i]);
                y=find(se[i]);
                v[x]=v[y]=1;
            }
            else
            {   
                if(fi[i]<0)fi[i]=-fi[i];
                if(se[i]<0)se[i]=-se[i];
                x=find(fi[i]);
                if(fi[i]==se[i])
                    v[x]=1;
                else
                {
                    y=find(se[i]);
                    if(x==y) v[x]=1;
                    else 
                    {
                        if(v[y])v[x]=1;
                        f[y]=x;
                    }
                }
            }
        }
        else
        {
            if(fi[i]<0)fi[i]=-fi[i];
            x=find(fi[i]);
            v[x]=1;
        }
    }
    printf(ok(n)?"YES\n":"NO\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值