• ZROI#999


    ZROI#999
    很有趣的一道题.本来我是想考虑枚举选几个盒子,但我发现这样并没有对问题有任何简化.
    然后就考虑容斥嘛...发现,这个容斥比较简单.
    假如令(f(S))(S)集合中的玩具不能选的方案数.
    那么答案就是:

    [sum_{ssubseteq T}{(-1)^{|S|}f(S)} ]

    那么(f(S))是啥呢?就是(2^{n-|S|}).
    然后直接套入就好.
    怎么理解呢?
    考虑容斥的基本形式:
    答案=总方案数-不满足一个限制的方案数+不满足两个限制的方案数-不满足三个限制的方案数...
    这不就是这个式子嘛....于是愉快(50pts).
    至于(n)高达(10^6)的时候,则需要使用(FMT)(即高维前缀和)来处理.
    (70pts)思路见代码后.
    (Code:)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <map>
    #include <set>
    #define MEM(x,y) memset ( x , y , sizeof ( x ) )
    #define rep(i,a,b) for (int i = a ; i <= b ; ++ i)
    #define per(i,a,b) for (int i = a ; i >= b ; -- i)
    #define pii pair < int , int >
    #define X first
    #define Y second
    #define rint read<int>
    #define int long long
    #define pb push_back
    
    using std::set ;
    using std::pair ;
    using std::max ;
    using std::min ;
    using std::priority_queue ;
    using std::vector ;
    using std::swap ;
    using std::sort ;
    using std::unique ;
    using std::greater ;
    
    template < class T >
        inline T read () {
            T x = 0 , f = 1 ; char ch = getchar () ;
            while ( ch < '0' || ch > '9' ) {
                if ( ch == '-' ) f = - 1 ;
                ch = getchar () ;
            }
           while ( ch >= '0' && ch <= '9' ) {
                x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
                ch = getchar () ;
           }
       return f * x ;
    }
    
    const int N = 1e6 + 100 ;
    const int mod = 1e9 + 7 ;
    
    int n , m , tmp[N] , cnt , num , ans ;
    bool box[N][25] ;
    
    inline int quick (int a , int p) {
        int res = 1 ;
        while ( p ) {
            if ( p & 1 ) res = ( res * a ) % mod ;
            a = ( a * a ) % mod ; p >>= 1 ;
        }
        return res ;
    }
    
    signed main (int argc , char * argv[]) {
        n = rint () ; m = rint () ; int maxsize = 1 << m ; rep ( i , 1 , n )
        { int k = rint () ; rep ( j , 1 , k ) { int x = rint () ; box[i][x] = true ; } }
        for (int i = 0 ; i < maxsize ; ++ i) {
            cnt = 0 ; num = 0 ;
            for (int j = 0 ; j < m ; ++ j)
                if ( i & ( 1 << j ) )
                    tmp[++cnt] = j + 1 ;
            rep ( j , 1 , n ) {
                bool f = true ;
                rep ( k , 1 , cnt ) if ( box[j][tmp[k]] ) f = false ;
                if ( f ) ++ num ;
            }
            int d = ( cnt & 1 ) ? - 1 : 1 ;
            ans = ( ans + d * quick ( 2 , num ) % mod ) % mod ;
        }
        printf ("%lld
    " , ( ans % mod + mod ) % mod ) ;
        system ("pause") ; return 0 ;
    }
    

    (70pts)并不难,
    不过我们的容斥会有些许变化.
    我们发现题目可以简化为:
    给定(n)个二进制数,任选一些数字,求有多少种选择方案使得选出来的数字按位or起来是每一位都是(1).
    于是我们就令(g(S))表示状态为(S)的盒子的个数.
    (f(T))表示(sum_{Ssubseteq T}g(S)).
    那么不难想到有这样一个容斥:

    [sum_{Ssubseteq T}{2^{f(S)}(-1)^{m-|S|}} ]

    那么我们考虑,这样求的复杂度怎么样.
    计算(g(S))是和读入一起的,所以复杂度是(O(n*m))的.
    而计算(f(T))呢?我们考虑这到底是个什么玩意儿.
    这其实是状态为(T)以及(T)的子集的盒子总数.
    所以我们要枚举子集的子集,这样的复杂度你可能会认为是(O(4^m))
    但其实不是,而是(O(3^m))的,这样的复杂度足以通过(70pts).
    满分思路见代码后.
    (Code:)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <map>
    #include <set>
    #define MEM(x,y) memset ( x , y , sizeof ( x ) )
    #define rep(i,a,b) for (int i = a ; i <= b ; ++ i)
    #define per(i,a,b) for (int i = a ; i >= b ; -- i)
    #define pii pair < int , int >
    #define X first
    #define Y second
    #define rint read<int>
    #define int long long
    #define pb push_back
    #define lowbit(x) ( ( x ) & ( - x ) )
    
    using std::set ;
    using std::pair ;
    using std::max ;
    using std::min ;
    using std::priority_queue ;
    using std::vector ;
    using std::swap ;
    using std::sort ;
    using std::unique ;
    using std::greater ;
    
    template < class T >
        inline T read () {
            T x = 0 , f = 1 ; char ch = getchar () ;
            while ( ch < '0' || ch > '9' ) {
                if ( ch == '-' ) f = - 1 ;
                ch = getchar () ;
            }
           while ( ch >= '0' && ch <= '9' ) {
                x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
                ch = getchar () ;
           }
       return f * x ;
    }
    
    const int N = 1e7 + 100 ;
    const int mod = 1e9 + 7 ;
    int n , g[N] , m , ans , f[N] , siz[N] , p[N] ;
    
    inline int quick (int a , int p) {
        int res = 1 ;
        while ( p ) {
            if ( p & 1 ) res = ( res * a ) % mod ;
            a = a * a % mod ; p >>= 1 ;
        }
        return res ;
    }
    
    signed main (int argc , char * argv[]) {
        n = rint () ; m = rint () ; int maxsize = 1 << m ;
        rep ( i , 1 , n ) {
            int k = rint () , res = 0 ;
            while ( k -- ) res |= ( 1 << ( rint () - 1 ) ) ;
            ++ g[res] ;
        }
        for (int i = 0 ; i < maxsize ; ++ i) {
            for (int j = i ; j ; j = ( j - 1 ) & i )
                f[i] += g[j] ;
            f[i] += g[0] ;
        }
        p[0] = 1 ; rep ( i , 1 , n ) p[i] = ( p[i-1] << 1 ) % mod ;
        for (int i = 1 ; i < maxsize ; ++ i) siz[i] = siz[i^lowbit(i)] + 1 ;
        for (int i = 0 ; i < maxsize ; ++ i) {
            int d = ( ( ( m - siz[i] ) & 1 ) ? - 1 : 1 ) ;
            ans = ( ( ans + p[f[i]] * d ) % mod + mod ) % mod ;
        }
        printf ("%lld
    " , ( ans % mod + mod ) % mod ) ;
        system ("pause") ; return 0 ;
    }
    

    我们发现上面的复杂度瓶颈其实是在求(f(T))的时候我们需要枚举子集的子集.
    那么我们就考虑能否优化掉这个复杂度.经过一番思考我们发现,这是个高维前缀和问题.
    所以需要使用(FMT),但这个题的(FMT)没有你想象的那个长得像(FFT)的亚子.
    这个题目用到的(FMT)是一种经典的形式:子集和形式.
    这里用到的十分简单,不需要卷积形式..
    然后你考虑,对于每一个集合,
    我们如果都能算出来它在每一位上分别缺一个(1)(即构成一个(size-1)的子集)的方案数之和,
    那么我们就能从(size)(1)的子集一直递推到(size)(m)的子集,于是我们就愉快的解决了这个问题.
    (Code:)

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <map>
    #include <set>
    #define MEM(x,y) memset ( x , y , sizeof ( x ) )
    #define rep(i,a,b) for (int i = a ; i <= b ; ++ i)
    #define per(i,a,b) for (int i = a ; i >= b ; -- i)
    #define pii pair < int , int >
    #define X first
    #define Y second
    #define rint read<int>
    #define int long long
    #define pb push_back
    #define lowbit(x) ( ( x ) & ( - x ) )
    
    using std::set ;
    using std::pair ;
    using std::max ;
    using std::min ;
    using std::priority_queue ;
    using std::vector ;
    using std::swap ;
    using std::sort ;
    using std::unique ;
    using std::greater ;
    
    template < class T >
        inline T read () {
            T x = 0 , f = 1 ; char ch = getchar () ;
            while ( ch < '0' || ch > '9' ) {
                if ( ch == '-' ) f = - 1 ;
                ch = getchar () ;
            }
           while ( ch >= '0' && ch <= '9' ) {
                x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
                ch = getchar () ;
           }
       return f * x ;
    }
    
    const int N = 1e7 + 100 ;
    const int mod = 1e9 + 7 ;
    int n , g[N] , m , cnt , num , ans , p[N] , siz[N] ;
    
    signed main (int argc , char * argv[]) {
        n = rint () ; m = rint () ; int maxsize = 1 << m ;
        rep ( i , 1 , n ) {
            int k = rint () , res = 0 ;
            while ( k -- ) res |= ( 1 << ( rint () - 1 ) ) ;
            ++ g[res] ;
        }
        for (int i = 0 ; i < m ; ++ i)
            for (int j = 0 ; j < maxsize ; ++ j)
                if ( j & ( 1 << i ) ) g[j] += g[j^(1<<i)] ;
        p[0] = 1 ; rep  ( i , 1 , n ) p[i] = ( p[i-1] << 1 ) % mod ;
        for (int i = 1 ; i < maxsize ; ++ i) siz[i] = siz[i^lowbit(i)] + 1 ;
        for (int i = 0 ; i < maxsize ; ++ i) {
            int d = ( ( ( m - siz[i] ) & 1 ) ? - 1 : 1 ) ;
            ans = ( ( ans + p[g[i]] * d ) % mod + mod ) % mod ;
        }
        printf ("%lld
    " , ( ans % mod + mod ) % mod ) ;
        system ("pause") ; return 0 ;
    }
    
    May you return with a young heart after years of fighting.
  • 相关阅读:
    python requests模块
    python 模拟豆瓣登录(豆瓣6.0)
    python 抓取糗事百科糗图
    python bz2模块
    the python challenge闯关记录(9-16)
    python之PIL库(Image模块)
    python之zipfile
    python之pickle模块
    the python challenge闯关记录(0-8)
    KVO简介
  • 原文地址:https://www.cnblogs.com/Equinox-Flower/p/11486647.html
Copyright © 2020-2023  润新知