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")<