CF_400_D

codeforces_400_D

题目大意:给出n扇门,m把钥匙,和没把钥匙可以改变状态(关->开,开->关》)的门的数量及对应编号(保证每个门被两把钥匙控制),现给出n扇门的初始状态(1表示开,0表示关),问是否可以通过这m把钥匙(可用可不用,不一定用完)使得所有的门都开启。
题解:终于看到了2-sat的能想出来的题了,2-sat本质是满族解的判断,这题显然可以用2-sat来求解,求解的一般套路是将每个点拆为两个点,分别表示取或者不取(即一件事的正反两面),最后通过2-sat算法,判断是否可行。这题需要一个转化,即我们对m拆点,表示用或者不用这把钥匙改变对应的门。那么如何连边?可以这样想:对于一个门 的初始状态,如果是开,那么两把钥匙要么都用,要么都不用;如果是关,一定是用一把弃一把。那么就是说连边是要用门的初始状态确定的,那么这题也就解决了。

#pragma comment(linker, "/STACK: 1024000000,1024000000")
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define em emplace
#define pii pair<int,int>
#define de(x) cout << #x << " = " << x << endl
#define clr(a,b) memset(a,b,sizeof(a))
#define INF (0x3f3f3f3f)
#define LINF ((long long)(0x3f3f3f3f3f3f3f3f))
#define F first
#define S second
using namespace std;
inline int getint()
{
  int _x=0; char _tc=getchar();
  while(_tc<'0'||_tc>'9') _tc=getchar();
  while(_tc>='0'&&_tc<='9') _x*=10,_x+=(_tc-'0'),_tc=getchar();
  return _x;
}

const int N = 1e5 + 15;
int n, m;
int dor[N];
vector<int> key[N];

struct TwoSet
{
	static const int MAXV = 100100;
	int V, n, tp, S[MAXV<<1];
	vector<int> g[MAXV<<1];
	bool vis[MAXV<<1];
	
	void init( int tt )
	{
		n = tt;
		V = (tt<<1) + 1;
		for ( int i = 1; i <= V; i ++ )
			g[i].clear(), vis[i] = false;
	}
	void addEdge( int x, int y )
	{
		g[x].pb(y);
		g[y].pb(x);
	}
	bool dfs( int x )
	{
		if ( vis[ x > n ? x - n : x + n ] ) return false;
		if ( vis[x] ) return true;
		vis[ S[++tp] = x ] = 1;
		for ( int v : g[x] )
			if ( !dfs(v) ) return false;
		return true;
	}
			
	bool solve()
	{
		for ( int i = 1; i <= n; i ++ )
			if ( !vis[i] && !vis[i+n] )
			{
				tp = 0;
				if ( !dfs(i) )
				{
					while ( tp ) vis[S[tp--]] = false;
					if ( !dfs(i+n) ) return false;
				}
			}
		return true;
	}
}TwoSet;

int main()
{
	scanf("%d%d", &n, &m);
	for ( int i = 1; i <= n; i ++ )
		scanf("%d", &dor[i]);
	for ( int i = 1, x; i <= m; i ++ )
	{
		scanf("%d", &x);
		for ( int j = 0, y; j < x; j ++ )
			scanf("%d", &y), key[y].pb(i);
	}
	TwoSet.init(m);
	for ( int i = 1, x, y; i <= n; i ++ )
	{
		x = key[i][0], y = key[i][1];
		if ( !dor[i] )
		{
			TwoSet.addEdge(x,y+m);
			TwoSet.addEdge(x+m,y);
		}
		else
		{
			TwoSet.addEdge(x,y);
			TwoSet.addEdge(x+m,y+m);
		}
	}
	if ( TwoSet.solve() ) puts("YES");
	else puts("NO");
	return 0;
}
posted @ 2019-02-25 20:50  FormerAutumn  阅读(125)  评论(0编辑  收藏  举报