Description
每扇门被两个开关控制,操作一个开关将改变所有它所控制的门的状态。求是否存在一个操作方案,打开所有的门。
Solution
对于开关设状态,(1) 表示操作这个开关,(0) 表示不操作
如果一扇门的状态是 (1),则控制它的两个开关状态必须相同((00/11))
如果一扇门的状态是 (0),则控制它的两个开关状态必须相反((01/10))
回顾一下 2-SAT 的建图方法
设有 (n) 个 (x_i),逻辑约束为一个主合取范式
每条边 (p o q) 表示选了 (p) 就必须要选 (q)
因此例如对于 (i or j),我们连边 ( eg i o j) 以及 ( eg j o i)
考虑每个变量
如果 (x o eg x),则 (x=0);如果 ( eg x o x),则 (x=1);如果 (x o eg x and eg x o x) 则无解
因此,如果任意一个变量 (x) 与 ( eg x) 在同一个 SCC 中,则无解,跑一遍 Tarjan 即可
回到本题,设某门对应的开关是 (p,q)
如果门是 (1),则 (p o q, q o p, eg p o eg q, eg q o eg p)
如果门是 (0),则 (p o eg q, q o eg p, eg p o q, eg q o p)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
namespace scc
{
vector <int> 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<g[p].size(); i++)
{
int q=g[p][i];
if(!dfn[q]) dfs(q), low[p]=min(low[p],low[q]);
else if(!bel[q]) low[p]=min(low[p],dfn[q]);
}
if(dfn[p]==low[p])
{
++tot;
for(int i=0; i!=p;)
{
i=s[top--];
bel[i]=tot;
scc[tot].push_back(i);
}
}
}
void solve(int _n)
{
n=_n;
for(int i=1; i<=n; i++) if(!dfn[i]) dfs(i);
}
bool getans()
{
for(int i=1;i<=n/2;i++)
{
if(bel[i]==bel[i+n/2]) return false;
}
return true;
}
}
using scc::make;
int n,m,r[N],p[N],q[N],t,k;
signed main()
{
ios::sync_with_stdio(false);
cin>>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")<<endl;
}