• [CF1400G]Mercenaries


    题目

    点这里看题目。

    分析

    这......看到 (m) 这么小,然后看到条件这么奇葩,显然是容斥计算。

    但是先不慌,我们先考虑在没有任何限制的时候该怎么计算。

    考虑枚举选的人数 (s) ,然后找出哪些佣兵在选的人数为 (s) 的时候可以被选,设为 (a_s) 。那么总的方案数就是:

    [sum_{s=1}^n inom{a_s}{s} ]

    会不会计算方案数直接决定了是你切这道题还是这道题切你。

    然后不难想到套上容斥。设选的人数为 (s) 时,会影响到选取的限制的集合为 (R_s) 。顺手定义一个 (cnt(R)) ,表示集合 (R) 中的限制会影响到的佣兵的数量。

    于是不难看出上面的式子可以扩展成为:

    [sum_{s=1}^n sum_{Tsubset R_s} (-1)^{|T|} inom{a_s-cnt(T)}{s-cnt(T)} ]

    就相当于钦定了 (T) 会影响到的人不选。

    然后注意到,容斥中枚举的实际上就是 (cnt(T)) 。因此改写式子:

    [sum_{s=1}^n sum_{c=0}^{2m} inom{a_s-c}{s-c}sum_{Tsubset R_s} [cnt(T)=c](-1)^{|T|} ]

    继续发现最后一层的式子的值仅与 (R_s)(c) 相关,且 (R_s) 可压缩、(c) 还很小。因此考虑预处理 (f)

    [f_{c,R}=sum_{Tsubset R}[cnt(T)=c](-1)^{|T|} ]

    不难想到设 (g_{c,T}=[cnt(T)=c](-1)^{|T|}) 。于是 (f_{c,R}) 就是 (g_{c,T}) 的子集前缀和(或者叫高维前缀和)。这个可以单层 (O(m2^m)) ,总时间 (O(m^22^m)) 地处理出来。

    然后用扫描线处理一下可选的佣兵的集合和数量,就可以得到每个 (s) 对应的 (R_s)(a_s) ,最后计算答案。时间复杂度是 (O(m^22^m+nm))

    小结:

    1. 计算方案数的基础方法。我感觉这就是本题的难点。本题的限制很特殊,因此可以考虑通过枚举变量来化开限制。
    2. 容斥思想和预处理系数。感觉很套路
    3. 高维前缀和的运用。

    代码

    #include <cstdio>
    #include <vector>
    #include <utility>
    using namespace std;
    
    typedef long long LL;
    typedef pair<int, int> Ristr;
    
    const int mod = 998244353;
    const int MAXN = 3e5 + 5, MAXM = 25, MAXS = ( 1 << 20 ) + 5;
    
    template<typename _T>
    void read( _T &x )
    {
        x = 0;char s = getchar();int f = 1;
        while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
        while( s >= '0' && 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 ) + 1; }
        if( 9 < x ){ write( x / 10 ); }
        putchar( x % 10 + '0' );
    }
    
    vector<int> ad[MAXN], del[MAXN]; 
    
    int f[MAXM << 1][MAXS];
    int fr[MAXM], to[MAXM];
    
    int fac[MAXN], ifac[MAXN];
    
    int id[MAXN];
    int N, M, cnt;
    bool app[MAXM << 1], in[MAXN];
    
    int Qkpow( int, int );
    int Lowbit( const int x ) { return x & ( -x ); }
    int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
    int Sub( int x, int v ) { return x < v ? x + mod - v : x - v; }
    int Mul( LL x, int v ) { x *= v; if( x >= mod ) x %= mod; return x; }
    int Add( int x, int v ) { return x + v >= mod ? x + v - mod : x + v; }
    int C( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }
    
    int Count( const int S )
    {
        for( int i = 1 ; i <= M << 1 ; i ++ ) app[i] = false;
        for( int i = 1 ; i <= M ; i ++ )
            if( S & ( 1 << i - 1 ) )
                app[id[fr[i]]] = true, app[id[to[i]]] = true;
        int tot = 0;
        for( int i = 1 ; i <= M << 1 ; i ++ )
            tot += app[i];
        return tot;
    }
    
    int Qkpow( int base, int indx )
    {
        int ret = 1;
        while( indx )
        {
            if( indx & 1 ) ret = Mul( ret, base );
            base = Mul( base, base ), indx >>= 1;
        }
        return ret;
    }
    
    void Init()
    {
        int upper = 1 << M, coe, tot;
        for( int S = 0 ; S < upper ; S ++ )
        {
            coe = 1, tot = Count( S );
            for( int i = 1 ; i <= M ; i ++ )
                if( S & ( 1 << i - 1 ) )
                    coe = mod - coe;
            f[tot][S] = Add( f[tot][S], coe );
        }
        for( int i = 0 ; i <= M << 1 ; i ++ )
            for( int k = 1 ; k < upper ; k <<= 1 )
                for( int S = k ; S < upper ; S ++ )
                    if( S & k )
                        f[i][S] = Add( f[i][S], f[i][S ^ k] ); 
        fac[0] = 1; for( int i = 1 ; i <= N ; i ++ ) fac[i] = Mul( fac[i - 1], i );
        ifac[N] = Inv( fac[N] ); for( int i = N - 1 ; ~ i ; i -- ) ifac[i] = Mul( ifac[i + 1], i + 1 );
    }
    
    int main()
    {
        read( N ), read( M );
        for( int i = 1, l, r ; i <= N ; i ++ )
            read( l ), read( r ), ad[l].push_back( i ), del[r + 1].push_back( i );
        for( int i = 1 ; i <= M ; i ++ )
            read( fr[i] ), read( to[i] ), id[fr[i]] = ++ cnt, id[to[i]] = ++ cnt;
        Init();
        int cur = 0, ans = 0, all = 0;
        for( int s = 1 ; s <= N ; s ++ )
        {
            cur = 0;
            for( int i = 0 ; i < ( int ) ad[s].size() ; i ++ ) in[ad[s][i]] = true, all ++;
            for( int i = 0 ; i < ( int ) del[s].size() ; i ++ ) in[del[s][i]] = false, all --;
            for( int i = 1 ; i <= M ; i ++ ) if( in[fr[i]] && in[to[i]] ) cur |= 1 << i - 1;
            for( int c = 0 ; c <= M << 1 ; c ++ )
                ans = Add( ans, Mul( C( all - c, s - c ), f[c][cur] ) );
        }
        write( ans ), putchar( '
    ' );
        return 0;
    }
    
  • 相关阅读:
    YII框架实现排序
    YII2 实现登录时候修改最新登录时间
    YII框架下实现密码修改
    json在PHP中应用技巧
    更换Python pip库镜像地址
    Python3创建RIDE桌面快捷方式的另一种方法
    谈谈测试人员的基本素养
    《微软的软件测试之道》阅读笔记
    PPT如何一页多张打印且铺满整个页面
    Linux 在线模拟器
  • 原文地址:https://www.cnblogs.com/crashed/p/13721119.html
Copyright © 2020-2023  润新知