[CF776D] The Door Problem - 2-SAT

Description

每扇门被两个开关控制,操作一个开关将改变所有它所控制的门的状态。求是否存在一个操作方案,打开所有的门。

Solution

对于开关设状态,\(1\) 表示操作这个开关,\(0\) 表示不操作

如果一扇门的状态是 \(1\),则控制它的两个开关状态必须相同(\(00/11\)

如果一扇门的状态是 \(0\),则控制它的两个开关状态必须相反(\(01/10\)


回顾一下 2-SAT 的建图方法

设有 \(n\)\(x_i\),逻辑约束为一个主合取范式

每条边 \(p \to q\) 表示选了 \(p\) 就必须要选 \(q\)

因此例如对于 \(i \or j\),我们连边 \(\neg i \to j\) 以及 \(\neg j \to i\)

考虑每个变量

如果 \(x\to \neg x\),则 \(x=0\);如果 \(\neg x \to x\),则 \(x=1\);如果 \(x \to \neg x \and \neg x \to x\) 则无解

因此,如果任意一个变量 \(x\)\(\neg x\) 在同一个 SCC 中,则无解,跑一遍 Tarjan 即可


回到本题,设某门对应的开关是 \(p,q\)

如果门是 \(1\),则 \(p \to q, q \to p,\neg p \to \neg q,\neg q \to \neg p\)

如果门是 \(0\),则 \(p \to \neg q, q \to \neg p, \neg p \to q,\neg q \to p\)

#include 
using namespace std;

#define int long long
const int N = 200005;

namespace scc
{
vector  g[N],scc[N];
int ind,f[N],siz[N],dfn[N],low[N],vis[N],s[N],bel[N],top,tot,n,m,d[N];
char ch[N];
void make(int p,int q)
{
    d[q]++;
    g[p].push_back(q);
}
void dfs(int p)
{
    s[++top]=p;
    dfn[p]=low[p]=++ind;
    for(int i=0; i>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>r[i];
    }
    for(int i=1;i<=m;i++)
    {
        cin>>t;
        while(t--)
        {
            cin>>k;
            if(p[k]==0) p[k]=i;
            else q[k]=i;
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(r[i])
        {
            make(p[i],q[i]);
            make(q[i],p[i]);
            make(p[i]+m,q[i]+m);
            make(q[i]+m,p[i]+m);
        }
        else
        {
            make(p[i],q[i]+m);
            make(q[i],p[i]+m);
            make(p[i]+m,q[i]);
            make(q[i]+m,p[i]);
        }
    }
    scc::solve(2*m);
    cout<<(scc::getans()?"YES":"NO")<

你可能感兴趣的:([CF776D] The Door Problem - 2-SAT)