[NOI2017]游戏

题目

点这里看题目。

分析

注意到,非 x 的赛道只有两种赛车可选,所以如果只有 a,b,c ,那么限制可以转化为 2-SAT 的边。下面我们用 ( eg h_i) 表示在 (i) 号位上不选择 (h_i) 这辆车。我们可以如下转化:

  1. 如果 ((i,h_i,j,h_j)) 中, (h_i) 本身不可选,该限制自然作废。
  2. 如果 ((i,h_i,j,h_j)) 中, (h_j) 本身不可选,那么 (h_i) 也不可选,即 (h_i ightarrow eg h_i)
  3. 如果 ((i,h_i,j,h_j)) 中, 两种车各自都可以选,那么显然连接 (h_i ightarrow h_j)( eg h_j ightarrow eg h_i)

现在考虑处理 x 。虽然我们不难想到 (O(3^d imes m)) 进行枚举,但是实际上我们只需要考虑 x 上选取 (A,B,C) 的情况。而 a 已经包含了选 (B,C)b 已经包含了选 (A,C) 。如果我们将 x 均替换为 ab ,那么我们已经考虑了所有的情况。因此只需要 (O(2^d imes m)) 便可以。

小结:

  1. x 替换做 a,b ,利用其含义进行 " 部分枚举 " ,而避免了直接枚举的高复杂度。

代码

#include <cstdio>

#define Opp( x ) ( x <= N ? x + N : x - N )

const int MAXN = 1e5 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct Edge
{
	int to, nxt;
}Graph[MAXN * 10];

struct Ristriction
{
	int x, y; char hx, hy;
	
	void Read() 
	{ 
		scanf( "%d %c %d %c", &x, &hx, &y, &hy );
		hx += 'a' - 'A', hy += 'a' - 'A';
	}
}R[MAXN];

int stk[MAXN], top;

int id[MAXN];
char S[MAXN], fir[3] = { 'b', 'a', 'a' }, avail[3][2] = { { 'b', 'c' }, { 'a', 'c' }, { 'a', 'b' } };
int seq[10], xtot;

int head[MAXN], DFN[MAXN], LOW[MAXN], bel[MAXN];
int N, M, D, cnt, tot, ID;
bool in[MAXN];

void AddEdge( const int from, const int to )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt;
}

void Tarjan( const int u )
{
	DFN[u] = LOW[u] = ++ ID;
	in[stk[++ top] = u] = true;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( ! DFN[v = Graph[i].to] ) Tarjan( v ), LOW[u] = MIN( LOW[u], LOW[v] );
		else if( in[v] ) LOW[u] = MIN( LOW[u], DFN[v] );
	if( DFN[u] == LOW[u] )
	{
		int v; tot ++;
		do in[v = stk[top --]] = false, bel[v] = tot;
		while( v ^ u );
	}
}

void Clean()
{
	ID = tot = top = cnt = 0;
	for( int i = 1 ; i <= N * 2 ; i ++ ) 
		head[i] = LOW[i] = DFN[i] = bel[i] = 0;
}

bool Chk()
{
	Clean();
	for( int i = 1 ; i <= N ; i ++ ) id[i] = S[i] - 'a';
	for( int i = 1 ; i <= M ; i ++ )
	{
		if( R[i].hx == S[R[i].x] ) continue;
		if( R[i].hy == S[R[i].y] ) 
		{
			int t = ( R[i].hx == fir[id[R[i].x]] ? R[i].x : Opp( R[i].x ) );
			AddEdge( t, Opp( t ) );
		}
		else 
		{
			int u = ( R[i].hx == fir[id[R[i].x]] ? R[i].x : Opp( R[i].x ) ),
				v = ( R[i].hy == fir[id[R[i].y]] ? R[i].y : Opp( R[i].y ) );
			AddEdge( u, v ), AddEdge( Opp( v ), Opp( u ) );
		}
	}
	for( int i = 1 ; i <= N * 2 ; i ++ ) if( ! DFN[i] ) Tarjan( i );
	for( int i = 1 ; i <= N ; i ++ ) if( bel[i] == bel[i + N] ) return false;
	return true;
}

int main()
{
	bool flg = false;
	read( N ), read( D );
	scanf( "%s", S + 1 ), read( M );
	for( int i = 1 ; i <= M ; i ++ ) R[i].Read();
	for( int i = 1 ; i <= N ; i ++ ) if( S[i] == 'x' ) seq[++ xtot] = i;
	for( int s = 0 ; s < ( 1 << xtot ) ; s ++ )
	{
		for( int i = 1 ; i <= xtot ; i ++ )
			S[seq[i]] = 'a' + ( s >> ( i - 1 ) & 1 );
		if( flg = Chk() ) break;
	}
	if( ! flg ) { puts( "-1" ); return 0; }
	for( int i = 1 ; i <= N ; i ++ )
		putchar( avail[id[i]][bel[i + N] < bel[i]] - 'a' + 'A' );
	putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/14116214.html