题意:有n扇门,每扇门的状态为开(0)或闭(1)。有m种操作,每种操作可以改变若干扇门的状态。每扇门恰好可以被两种不同的操作改变状态。给出n扇门的初始状态,问是否可以经过操作把所有的门的状态都改为闭?
分析:设可改变第i扇门的2个操作为ai和bi。若第i扇门的状态为开,则ai和bi操作恰好只能执行一个;若第i扇门的状态为闭,则ai和bi操作要么都不执行要么都执行。由此原问题转化为2-sat问题。由于所加的边比较特殊(都是无向边),所以直接用并查集来维护强连通分量即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,m,a[maxn],mp[maxn][2];
int pre[maxn];
int find(int u)
{
if (pre[u]!=u) pre[u]=find(pre[u]);
return pre[u];
}
void add(int u,int v)
{
u=find(u);v=find(v);
pre[u]=v;
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=1-a[i];
for (int i=1;i<=m;i++)
{
int tot;scanf("%d",&tot);
while (tot--)
{
int x;scanf("%d",&x);
if (!mp[x][0]) mp[x][0]=i;
else mp[x][1]=i;
}
}
for (int i=1;i<=2*m;i++) pre[i]=i;
for (int i=1;i<=n;i++)
if (a[i])
{
add(mp[i][0],m+mp[i][1]);
add(m+mp[i][0],mp[i][1]);
}
else
{
add(mp[i][0],mp[i][1]);
add(m+mp[i][0],m+mp[i][1]);
}
bool flag=1;
for (int i=1;i<=m;i++) if (find(i)==find(m+i)) flag=0;
if (flag) cout<<"YES";
else cout<<"NO";
return 0;
}