1. 程式人生 > 其它 >Solution -「NOI 2021」「洛谷 P7740」機器人遊戲

Solution -「NOI 2021」「洛谷 P7740」機器人遊戲

\(\mathcal{Description}\)

  Link.

  自己去讀題面叭~

\(\mathcal{Solution}\)

  首先,參悟【樣例解釋 #2】。一種暴力的思路即為欽定集合 \(S\) 內的位置都合法,容斥計數。其中對於每條紙帶的每個位置,有三種情況(令 _ 為“保持不變”,注意沒有被機器人經過的位置都有這種修改):

  • 同時存在 _*;或者同時存在 01:只能為空,方案數為 \(1\)
  • 否則,存在(_*)且存在(01):只能為空或 01 中的一個,方案數為 \(2\)
  • 否則,方案數為 \(3\)

  暴力做的複雜度是 \(\mathcal O(2^nn^am^b)\)

\(a,b\) 看細節實現,不太可觀。

  嘗試欽定最後一個在 \(S\) 中的位置為 \(r\),那麼所有修改長度大於 \(n-r+1\) 的機器人所在的紙帶都必須全空。在此限制下帶著容斥係數狀壓 DP,令 \(f(i,S)\) 表示考慮了紙帶的位置 \(i\),前若干個位置的選擇情況為 \(S\),注意此處 \(|S|\le2^{\min\{i,n-r\}}=\mathcal O(2^{n/2})\),轉移時可以 \(\mathcal O(nm)\) 暴力計算當前位置的貢獻,得到了 \(\mathcal O(2^{n/2}nm)\) 的演算法。

  順帶,注意到統計當前位置貢獻時可以描述為形如“有多少個機器人的修改序列中,下標屬於 \(S\)

的位置上存在 _/*/0/1”,可以用 std::bitset 優化。最終複雜度為 \(\mathcal O(\frac{2^{n/2}nm}{\omega})\)

\(\mathcal{Code}\)

/*~Rainybunny~*/

#include <bitset>
#include <cstdio>
#include <cassert>
#include <cstring>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef unsigned long long ULL;

const int MAXN = 32, MAXM = 1e3, MOD = 1e9 + 7;
int n, m, pw2[MAXM + 5], pw3[MAXM + 5];

inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
inline int mul( const int a, const int b ) { return int( 1ll * a * b % MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }

struct Robot {
	int len, val[MAXN + 5];
	inline int operator [] ( const int k ) const {
		return k <= len ? val[k] : 0;
	}
	inline void read() {
		static char str[105]; scanf( "%s", str );
		val[len = 1] = 0;
		for ( int i = 0; str[i]; ++i ) {
			if ( str[i] == 'R' ) val[++len] = 0;
			else if ( str[i] == '0' ) val[len] = 1;
			else if ( str[i] == '1' ) val[len] = 2;
			else val[len] = 3 - val[len];
		}
	}
} rbt[MAXM + 5];

inline int solve( const int r ) {
	static int f[2][1 << 17][2];
	static std::bitset<MAXM + 5> sig[MAXN + 5][4], g[1 << 17][4];
	int allc = 0;
	
	rep ( i, 0, n - 1 ) rep ( j, 0, 3 ) sig[i][j].reset();
	rep ( i, 1, m ) if ( rbt[i].len <= n - r + 1 ) {
		++allc;
		rep ( j, 0, n - 1 )
			sig[j][rbt[i][j + 1]].set( i );
	}
	rep ( i, 0, imin( n - r, r ) ) rep ( j, 0, 3 ) {
		g[1 << i][j] = sig[i][j];
	}
	
	rep ( S, 1, ( 1 << imin( n - r, r ) << 1 ) - 1 ) if ( S & ( S - 1 ) ) {
		rep ( i, 0, 3 ) {
			g[S][i] = g[S ^ ( S & -S )][i] | g[S & -S][i];
		}
	}
	
	auto calc = [&]( const bool sty, const ULL S )->int {
		static std::bitset<MAXM + 5> x, y;
		x = ( sty ? g[S][3] : g[S][0] & g[S][3] ) | ( g[S][1] & g[S][2] );
		y = ~x & ( sty ? g[S][1] | g[S][2] :
		  ( g[S][0] | g[S][3] ) & ( g[S][1] | g[S][2] ) );
		int t = y.count();
		return mul( pw2[t], pw3[allc - t - x.count()] );
	};
	
	memset( f[0], 0, sizeof f[0] ), f[0][0][0] = 1;
	for ( int i = 0, sta = 0; i < r; ++i, sta ^= 1 ) {
		memset( f[!sta], 0, sizeof f[!sta] );
	  	rep ( S, 0, ( 1 << imin( i, n - r ) ) - 1 ) {
			ULL T = ULL( S ) << 1ull & ( ( 1ull << n >> r ) - 1 );
			bool flg = ULL( S ) << 1ull << r >> n & 1ull;
			
			rep ( t, 0, 1 ) {
				int cur = f[sta][S][t]; f[sta][S][t] = 0;
				if ( !cur ) continue;
				subeq( f[!sta][r == n ? 0 : T | 1][t || flg || r == n],
				  mul( cur, calc( t || i + 1 < r, S << 1 | 1 ) ) );
				if ( i + 1 < r ) {
					addeq( f[!sta][T][t || flg],
					  mul( cur, calc( 1, S << 1 ) ) );
				}
			}
		}
	}

	int ret = 0;
	rep ( t, 0, 1 ) {
		for ( int d = 1, sta = r & 1; d <= n - r; ++d, sta ^= 1 ) {
			rep ( i, 0, imin( r, n - r ) - 1 ) {
				rep ( j, t, 3 ) g[1 << i][j] = sig[i + d][j];
			}
			rep ( S, 0, ( 1 << imin( r, n - r ) ) - 1 ) {
				if ( S & ( S - 1 ) ) {
					rep ( j, t, 3 ) {
						g[S][j] = g[S ^ ( S & -S )][j] | g[S & -S][j];
					}
				}
				f[!sta][S][t] = mul( f[sta][S][t], calc( t, S ) );
				f[sta][S][t] = 0;
			}
		}
		rep ( S, 0, ( 1 << imin( r, n - r ) ) - 1 ) {
			addeq( ret, f[n & 1][S][t] );
		}
	}
	return ret;
}

int main() {
    // freopen( "robot.in", "r", stdin );
    // freopen( "robot.out", "w", stdout );
    
	scanf( "%d %d", &n, &m );
	pw2[0] = pw3[0] = 1;
	rep ( i, 1, m ) {
		rbt[i].read();
		pw2[i] = mul( 2, pw2[i - 1] ), pw3[i] = mul( 3, pw3[i - 1] );
	}
	
	int ans = 0;
	rep ( i, 1, n ) subeq( ans, solve( i ) );
	printf( "%d\n", ans );
	return 0;
}