1. 程式人生 > 其它 >「ARC105F」Lights Out on Connected Graph

「ARC105F」Lights Out on Connected Graph

題目

點這裡看題目。

分析

手玩容易發現 good graph 的第二條要求等價於 \(G'\) 是二分圖。

說明:

\(x_u\) 表示某種方案中 \(u\) 是否被操作。

那麼有 \(|E'|\) 條方程。對於 \((u,v)\in E'\),方程的形式為 \(x_u\oplus x_v=1\)

取出任意的相鄰兩條邊,比如 \((u,v),(v,w)\),將兩條方程異或起來得到 \(x_u=x_w\)

那麼 \(x\) 對應了一種黑白染色方案,而當且僅當 \(G'\) 是二分圖的時候,才會存在合法的 \(x\)

考慮二分圖比較好用的性質:有且僅有二分圖可以被黑白染色。那麼限制變成:

  • \(G'\) 連通;
  • \(G'\) 可以被黑白染色;

注意到“可以被黑白染色”這條限制明顯比“連通”難處理得多,因此我們優先處理掉第二條限制

\(G[S]\) 為點集 \(S\)\(G\) 上的匯出子圖,\(G[S]=(S,E[S])\)。我們可以設 \(f_S\) 為所有 \(G'=(S,E'),E'\subseteq E[S]\)黑白染色方案的數量之和。這個很容易計算,我們只需要列舉哪些染白、哪些染黑,則邊一定從跨黑白的邊之中選出。

\(f\) 中的圖有些是不連通的,我們需要再將它們處理掉。這很容易,我們只需要設 \(g_S\) 為所有連通的 \(G'\) 的黑白染色方案的數量之和即可,而 \(g\)

也可以使用 DP 簡單地計算。我們可以欽定元素 \(k\in S\),則有:

\[g_S=f_S-\sum_{k\in T\subsetneq S}g_T\times f_{S\setminus T} \]

注意到,連通的二分圖只有兩種黑白染色方案,那麼答案即為 \(\frac{1}{2}g_{U}\)

小結:

  1. 限制較多的時候,一定要注意哪些限制比較難以處理,難以處理的限制一般會通過列舉優先保證的方式來解決;
  2. 本題中,我們沒有選擇直接對圖計數,而是對圖染色方案數計數,最終利用性質可以簡單地推出答案。很多時候不一定可以方便地直接算出答案,我們最好是計算某些易於計算的量再較快地推出答案

程式碼

#include <cstdio>
 
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
 
const int mod = 998244353;
const int MAXN = 20, MAXM = 17 * 16 + 5, MAXS = ( 1 << 17 ) + 5;
 
int grp[MAXS];
int f[MAXS], g[MAXS];
int pw[MAXM];
 
int N, M;
 
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
 
int main()
{
	scanf( "%d %d", &N, &M );
	rep( i, 1, M )
	{
		int a, b;
		scanf( "%d %d", &a, &b ), a --, b --;
		for( int S = 0 ; S < ( 1 << N ) ; S ++ )
			if( ( S >> a & 1 ) && ( S >> b & 1 ) )
				grp[S] ++;
	}
	pw[0] = 1; rep( i, 1, M ) pw[i] = Mul( pw[i - 1], 2 );
	for( int S = 0 ; S < ( 1 << N ) ; S ++ )
	{
		g[S] = 1;
		for( int T = S ; T ; T = ( T - 1 ) & S )
			g[S] = Add( g[S], pw[grp[S] - grp[T] - grp[S ^ T]] );
	}
	f[0] = 1;
	for( int S = 1 ; S < ( 1 << N ) ; S ++ )
	{
		int low = S & ( - S ); f[S] = g[S];
		for( int T = ( S - 1 ) & S ; T ; T = ( T - 1 ) & S )
		{
			if( ! ( T & low ) ) continue;
			f[S] = Sub( f[S], Mul( f[T], g[S ^ T] ) );
		}
	}
	printf( "%d\n", Mul( f[( 1 << N ) - 1], ( mod + 1 ) >> 1 ) );
	return 0;
}