1. 程式人生 > 實用技巧 >[NOI2017]遊戲

[NOI2017]遊戲

題目

點這裡看題目。

分析

注意到,非 x 的賽道只有兩種賽車可選,所以如果只有 a,b,c ,那麼限制可以轉化為 2-SAT 的邊。下面我們用 \(\neg 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\rightarrow \neg h_i\)
  3. 如果 \((i,h_i,j,h_j)\) 中, 兩種車各自都可以選,那麼顯然連線 \(h_i\rightarrow h_j\)
    \(\neg h_j\rightarrow \neg h_i\)

現在考慮處理 x 。雖然我們不難想到 \(O(3^d\times m)\) 進行列舉,但是實際上我們只需要考慮 x 上選取 \(A,B,C\) 的情況。而 a 已經包含了選 \(B,C\)b 已經包含了選 \(A,C\) 。如果我們將 x 均替換為 ab ,那麼我們已經考慮了所有的情況。因此只需要 \(O(2^d\times 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( '\n' );
	return 0;
}