zoukankan      html  css  js  c++  java
  • 「ARC105F」Lights Out on Connected Graph

    题目

    点这里看题目。

    分析

    手玩容易发现 good graph 的第二条要求等价于 (G') 是二分图。

    说明:

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

    那么有 (|E'|) 条方程。对于 ((u,v)in E'),方程的形式为 (x_uoplus 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 简单地计算。我们可以钦定元素 (kin S),则有:

    [g_S=f_S-sum_{kin Tsubsetneq S}g_T imes f_{Ssetminus 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
    ", Mul( f[( 1 << N ) - 1], ( mod + 1 ) >> 1 ) );
    	return 0;
    }
    
  • 相关阅读:
    序一
    Python3 网络爬虫开发实战
    爬虫笔记
    celery
    用yield实现协程 和asyncio模块
    Django学习之完成数据库主从复制、读写分离和一主多从情况下的使用办法
    Django学习之缓存和信号
    Django学习之Django-debug-toobar
    Python面向对象
    Python中 if __name__ == "__main__" 的理解
  • 原文地址:https://www.cnblogs.com/crashed/p/15120997.html
Copyright © 2011-2022 走看看