三值逻辑-并查集

三值逻辑-并查集

P9869 三值逻辑

题意

在三值逻辑中, \(T\) 为真, \(F\) 为假,\(U\) 为未确定,定义 \(\lnot U = U\),接下来给出 \(n\) 个值的 \(m\) 条赋值表达式,最后的值等于初始值的情况下,问最少有几个 \(U\)

思路

看着这种相互取反,很容易想到使用扩展域并查集(二分图我们想到)。但是我在考试的时候总想着怎么把赋值语句在并查集中表达出来,虽然是错误的,但我还是想要分享一下。很容易发现如果 \(a\) 在给其他位置数赋值后被其他位置赋值,则这个 \(a\) 不应该和之前的点在一个并查集,但它的儿子却不应该动。所以我想到了原本的两个根并,变成了一个点直接被接到另一个根下,但如果没被赋值过,则它应该带着儿子,否则不能。这里带不带就成了我的思路的实现难点。

现在来说一下正解。容易发现我们的初始状态只和最终状态(关系)有关废话,直接在赋值的时候维护整个并查集并不好写,但是只维护这个点位置上的值(关系)很好写。简单维护出每个点上的关系之后放扩展域并查集就很好写了。

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn = 3e5+10;
void read(int &);

int sub;
int fa[maxn];
int n,m;
int zhi[maxn];

inline void init(int n)
{
    for(int i=1,i_ed=n*2+4 ;i<=i_ed;++i)
    {
        fa[i]=i;
        zhi[i]=i;
    }
    //2*n+1-T  2*n+2-F 2*n+3-U
}

int finr(int x)
{
    return x==fa[x] ? x : fa[x]=finr(fa[x]);
}

inline void join(int x,int y)
{
    int xr=finr(x);
    int yr=finr(y);
    if(xr!=yr)
    {
        fa[xr]=yr;
    }
}

inline int opp(int x)
{
    if(x>2*n)
    {
        if(x==2*n+1)// 这里要注意F和T的取反
        {
            return x+1;
        }
        else if(x==2*n+2)
        {
            return x-1;
        }
        return x;
    }
    if(x>n)
    {
        return x-n;
    }
    return x+n;
}

void solve()
{
    read(n);
    read(m);
    init(n);
    int T=2*n+1;
    int F=2*n+2;
    int U=2*n+3;

    char c;
    for(int i=1,u,v ;i<=m;++i)
    {
        scanf(" %c",&c);
        read(v);
        if(c=='T')
        {
            zhi[v]=T;
            zhi[v+n]=F;
        }
        else if(c=='F')
        {
            zhi[v]=F;
            zhi[v+n]=T;
        }
        else if(c=='U')
        {
            zhi[v]=U;
            zhi[v+n]=U;
        }
        else if(c=='+')
        {
            read(u);
            zhi[v+n]=opp(zhi[u]);// 这里注意先作v+n,因为v可能等于u
            zhi[v]=zhi[u];
        }
        else if(c=='-')
        {
            read(u);
            zhi[v+n]=zhi[u];
            zhi[v]=opp(zhi[u]);
        }
    }
    for(int i=1;i<=n;++i)
    {
        join(i,zhi[i]);
        join(i+n,zhi[i+n]);
    }
    for(int i=1;i<=n;++i)
    {
        if(finr(i)==finr(i+n))
        {
            join(i,U);
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i)
    {
        if(finr(i)==finr(U))
        {
            ++ans;
        }
    }
    printf("%lld\n",ans);
}

signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("tribool.in","r",stdin);
    freopen("tribool.out","w",stdout);
    #endif // ONLINE_JUDGE

    int t;
    read(sub);
    read(t);
    while(t--)
    {
        solve();
    }

    return 0;
}

inline void read(int &x)
{
    x=0;
    int f=1;
    signed c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
        {
            f=-1;
        }
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    x*=f;
}
posted @ 2025-11-26 18:12  玖玮  阅读(7)  评论(0)    收藏  举报