链接:https://ac.nowcoder.com/acm/contest/327/F
来源:牛客网
题目描述
处女座进行了一次探险,发现了一批宝藏。如果他获得这批宝藏,那么他一辈子都不需要工作了。但是处女座遇到了一个难题。
宝藏被装在n个宝箱里,宝箱编号为1,2,…,n,只有所有宝箱在某一时间被打开,处女座才能获得宝藏。有m个开关,每个开关控制k个宝箱,如果按下一个开关,那么这k个宝箱的开关状态都会发生改变(从开启变成关闭,从关闭变成开启),处女座想知道他能否获得这批宝藏。
输入描述:
第一行两个正整数n,m, 第二行n个整数,每个整数为0或1,表示初始时宝箱的状态,0表示开启,1表示关闭 接下来m行,每行开头一个整数k表示这个开关控制的宝箱的个数,接下来k个整数,表示控制宝箱的编号 1<=n,m<=200000 1<=k<=n 题目保证每个宝箱最多被两个开关控制。
输出描述:
一行,如果处女座能获得宝藏,输出”YES”,否则输出”NO”
示例1
输入
复制
4 4 1 0 1 1 2 3 4 2 1 3 1 2 2 1 2
输出
复制
YES
思路:
sat的问题。
首先每个宝箱最多只有两个开关,所以2-sat问题可以做。
2-sat判断有解的建图,一般就是要保证合法,当不合法时,一定要使得这个点的选与不选在一个连通块里。
x代表开关开的点,x^1代表开关关的点。
1. 当一个宝箱被2个开关所控制。
如果初始状态时关闭的。那么如果选择了x开关,必定选择y^1开关。选y,则选x^1.(确保宝箱是开启的)
那么如果选择了x^1开关,必定选择y开关。选y,^1则选x.
如果初始状态时开启的。那么如果选择了x开关,必定选择y开关。选y,则选x.
那么如果选择了x^1开关,必定选择y^1开关。选y^1,则选x^1.
2. 当一个宝箱被1个开关所控制。
如果初始状态时关闭的。那么如果选择了x^1开关,必定选择x开关。(确保一定不选x)
如果初始状态时开启的。那么如果选择了x开关,必定选择x^1开关。(确保一定选x)
3. 当一个宝箱被0个开关控制时。
如果初始状态时关闭的。直接输出NO。
如果初始状态时开启的。不需要管它了。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <cmath>
#include <stack>
#include <ctime>
using namespace std;
#define ll long long
const int maxn=4e5+100;
const int maxm=8e5+100;
int n,m,a[maxm][3];
struct note
{
int to;
int next;
}edge[maxm];
int head[maxn];
int ip;
int dfn[maxn],low[maxn],sccno[maxn],cnt,scc,instack[maxn];
stack<int>stk;
void init()
{
memset(head,-1,sizeof(head));
ip=0;
}
void addedge(int u,int v)
{
edge[ip].to=v,edge[ip].next=head[u],head[u]=ip++;
}
// x -> !y y -> !x
void add_1(int x,int y)
{
addedge(x,y^1);
addedge(y,x^1);
}
// x -> y y -> x
void add_2(int x,int y)
{
addedge(x,y);
addedge(y,x);
}
void dfs(int u)
{
dfn[u]=low[u]=++scc;
stk.push(u);
instack[u]=1;
for (int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].to;
if (!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if (instack[v])
low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u])
{
cnt++;
int x;
do
{
x=stk.top();
stk.pop();
sccno[x]=cnt;
instack[x]=0;
}while (x!=u);
}
}
bool solve()
{
scc=cnt=0;
memset(sccno,0,sizeof(sccno));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instack,0,sizeof(instack));
while (!stk.empty()) stk.pop();
for (int i=0; i<2*m; i++) if (!dfn[i]) dfs(i);
for (int i=0; i<2*m; i+=2)
{
if (sccno[i]==sccno[i^1]) return false;
}
return true;
}
int w[maxn];
vector<int>G[maxn];
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
for(int i=0;i<m;i++)
{
int k;
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
int v;
scanf("%d",&v);
G[v].push_back(i);
}
}
int flag=0;
for(int i=1;i<=n;i++)
{
int l=G[i].size();
if(l==2)
{
int x=G[i][0]*2;
int y=G[i][1]*2;
if(w[i]==1)
{
add_1(x,y);
add_1(x^1,y^1);
}
else
{
add_2(x,y);
add_2(x^1,y^1);
}
}
else if(l==1)
{
int x=G[i][0]*2;
if(w[i]==1)
{
addedge(x^1,x);
}
else
{
addedge(x,x^1);
}
}
else if(l==0)
{
if(w[i]==1)
{
flag=1;
}
}
}
if(flag)
{
printf("NO\n");
return 0;
}
if(solve())
{
printf("YES\n");
}
else printf("NO\n");
return 0;
}