动态规划专题--容斥原理--codeforces-285E Positions in Permutations

codeforces-285E \(Positions \ in \ Permutations\)

$$codeforces$$

题意

给定一个序列长度为 \(n\) 的序列 , \(A=\{1 \dots n\}\)

对于 \(A\) 的排列 \(P\) , 位置 \(i\) 的值为 \(p_i\) , 定义这个点为好的满足以下条件:

\[|p_i - i| = 1 \]

\(p_i = i + 1 \ or \ i - 1\)

问对于 \(A\) 的排列中恰好有 \(K\) 个好点的排列数。

\(k \le n \le 1000\) .

题解

定义 \(dp_{i , j , l_{(2)} , k_{(2)}}\) 为长度为 \(i\) , 有 \(j\) 个点已经被挑出作为好点的 \(\bf{取法}\) ( 或者理解为至少有 \(j\) 个好点 ) , \(l\) 表示 \(i\) 是否被选 , \(k\) 表示 \(i + 1\) 是否被选.

我们可以获得如下转移方程:

dp[i][j][0][0] = ( dp[i][j][0][0] + dp[i - 1][j][0][0] + dp[i - 1][j][1][0] ) % mod ; 
dp[i][j][1][0] = ( dp[i][j][1][0] + dp[i - 1][j][0][1] + dp[i - 1][j][1][1] ) % mod ; 

if ( j ) {
    dp[i][j][0][0] = ( dp[i][j][0][0] + dp[i - 1][j - 1][0][0] ) % mod ; 
    dp[i][j][1][0] = ( dp[i][j][1][0] + dp[i - 1][j - 1][0][1] ) % mod ; 
    
    if( i != n ) {
        dp[i][j][0][1] = ( dp[i][j][0][1] + dp[i - 1][j - 1][1][0] + dp[i - 1][j - 1][0][0] ) % mod ; 
        dp[i][j][1][1] = ( dp[i][j][1][1] + dp[i - 1][j - 1][1][1] + dp[i - 1][j - 1][0][1] ) % mod ; 
    }

}

解释一下:

\(i\) 位置不选时 , 考虑得到:

\[dp_{i , j , 0 , 0} += dp_{i - 1 , j , 1 , 0} + dp_{i - 1 , j , 0 , 0} \]

\[dp_{i , j , 1 , 0} += dp_{i - 1 , j , 0 , 1} + dp_{i - 1 , j , 1 , 1} \]

\(i\) 位置选 \(i - 1\) :

\[dp_{i , j , 1 , 0} += dp_{i - 1 , j - 1 , 0 , 0} \]

\[dp_{i , j , 1 , 1} += dp_{i - 1 , j - 1 , 0 , 1} \]

\(i\) 位置选 \(i + 1\) :

\[dp_{i , j , 0 , 1} += dp_{i - 1 , j - 1 , 0 , 0} + dp_{i - 1 , j - 1 , 1 , 0} \]

\[dp_{i , j , 1 , 1} += dp_{i - 1 , j - 1 , 0 , 1} + dp_{i - 1 , j - 1 , 1 , 1} \]

仔细思考易得其正确性。

设一个 \(ans_i\) 表示长度为 \(n\) , 有至少 \(i\) 个好点排列数,易得:

\[ans_i = dp_{n , i , 0 , 0} + dp_{n , i , 1 , 0} \times (n - i)! \]

易得最后答案为:

\[answer = \sum^{n}_{i = K}(-1)^{i - K} \times C^{K}_{i} \times ans_i \]

code

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std ; 
const int N = 1e3 + 10 ; 
const int mod = 1e9 + 7 ; 

int n , m , K ; 
int dp[N][N][2][2] , ans[N] , answer ; 

inline int read() {
    int x = 0 , f = 1 ; 
    char c = getchar() ; 

    while ( c < '0' || c > '9' ) {

        if ( c == '-' ) f = -f ; 

        c = getchar() ; 
    }

    while ( c >= '0' && c <= '9' ) {
        x = x * 10 + c - '0' ; 
        c = getchar() ; 
    }

    return x * f ; 
}

namespace Combination {
    int D[N] ; int nueyong[N] , sum_neo[N] , sum[N] ; 

    inline void lear_neoyong() {

        sum_neo[0] = sum_neo[1] = 1 ; 
        nueyong[1] = 1 ; nueyong[0] = 1 ; 
        sum[0] = sum[1] = 1 ; 

        for( int i = 2 ; i < N ; ++ i ) {
            
            int p = mod ; 
            int k = p / i ; 
            nueyong[i] = ( k * ( p - nueyong[p % i] ) ) % p ; 
            sum_neo[i] = ( nueyong[i] * sum_neo[i - 1] ) % p ; 
            sum[i] = ( i * sum[i - 1] ) % p ; 
        
        }
    }

    int Quick_Pow( int alpha , int beta ) {
        int ans = 1 ; 

        while ( beta > 0 ) { 
            
            if( beta & 1 ) ans = ( ans * alpha ) % mod ; 
            
            beta >>= 1 ; alpha = ( alpha * alpha ) % mod ;  
        }
        
        return ans ; 
    }

    int Regular_C_of_Pow_Class( int n , int m ) {
        int alpha = 1 , beta = 1 , rereturn = 0 ; 
        
        if( m <= n && n >= 0 && m >= 0 ) {
            
            for( int i = n - m + 1 ; i <= n ; ++ i ) {
                alpha = ( alpha * i ) % mod ; 
            
            }
            for( int i = 1 ; i <= m ; ++ i ) {
                beta = ( beta * i ) % mod ; 
            }   
            
            rereturn = ( alpha * Quick_Pow( beta , mod - 2 ) ) % mod ;  
            return rereturn ; 
        
        }
        
        else return 0 ; 
    
    }

    inline int jc( int x ) {
        return sum[x] ; 
    }

    inline int neo_jc( int x ) {  
        
        if ( x == 0 ) return 1 ; 
        
        return sum_neo[x] ; 
    }

    int Regular_C_of_Inv( int n , int m ) {
        return ( ( ( jc( n ) * neo_jc( n - m ) ) % mod ) * neo_jc( m ) ) % mod ; 
    }

    int C_Lucas_Using_Inv( int n , int m ) {
        
        if ( m > n ) return 0 ; 

        if ( m == 0 ) return 1 ; 
        
        return ( Regular_C_of_Inv( n % mod , m % mod ) * C_Lucas_Using_Inv( n / mod , m / mod ) ) % mod ; 
    }

    int C_Lucas_Using_Pow( int n , int m ) {
        
        if( m == 0 ) return 1 ; 

        return ( Regular_C_of_Pow_Class( n % mod , m % mod ) * C_Lucas_Using_Pow( n / mod , m / mod ) ) % mod ; 
    }

    void Asking_for_Derangement() {

        D[0] = 1 ; 
        D[1] = 0 ; 
        D[2] = 1 ; 
        for( int i = 3 ; i < N ; ++ i ) {

            D[i] = ( i - 1 ) * ( D[i - 1] + D[i - 2] ) % mod ; 
        
        }
    }

    int Regular_A( int n , int m ) {
        return ( jc( n ) * neo_jc( n - m ) ) % mod ; 
    }

    inline void Cleared() {
        memset( D , 0 , sizeof(D) ) ; 
        memset( sum_neo , 0 , sizeof(sum_neo) ) ; 
        memset( sum , 0 , sizeof(sum) ) ;  
        memset( nueyong , 0 , sizeof(nueyong) ) ; 
    }
} ; 
using namespace Combination ; 

signed main() {
    
    #ifndef ONLINE_JUDGE
        freopen( "1.in" , "r" , stdin ) ; 
        freopen( "1.out", "w" ,stdout ) ; 
    #endif

    n = read() , K = read() ; 

    lear_neoyong() ; 
    dp[1][0][0][0] = 1 ; dp[1][1][0][1] = 1 ; 

    for ( int i = 2 ; i <= n ; ++ i ) {
        for ( int j = 0 ; j <= n ; ++ j ) {
            dp[i][j][0][0] = ( dp[i][j][0][0] + dp[i - 1][j][0][0] + dp[i - 1][j][1][0] ) % mod ; 
            dp[i][j][1][0] = ( dp[i][j][1][0] + dp[i - 1][j][0][1] + dp[i - 1][j][1][1] ) % mod ; 

            if ( j ) {
                dp[i][j][0][0] = ( dp[i][j][0][0] + dp[i - 1][j - 1][0][0] ) % mod ; 
                dp[i][j][1][0] = ( dp[i][j][1][0] + dp[i - 1][j - 1][0][1] ) % mod ; 
                
                if( i != n ) {
                    dp[i][j][0][1] = ( dp[i][j][0][1] + dp[i - 1][j - 1][1][0] + dp[i - 1][j - 1][0][0] ) % mod ; 
                    dp[i][j][1][1] = ( dp[i][j][1][1] + dp[i - 1][j - 1][1][1] + dp[i - 1][j - 1][0][1] ) % mod ; 
                }

            }

        }
    }

    for ( int i = 0 ; i <= n ; ++ i ) {
        ans[i] = ( jc( n - i ) * ( dp[n][i][0][0] + dp[n][i][1][0] ) ) % mod ; 
        // cout << i << ' ' << ans[i] << '\n' ; 
    }

    int f = - 1 ; 
    for ( int i = K ; i <= n ; ++ i ) {
        f = -f ;  
        answer = ( answer + ( ( ( f * ( ans[i] * C_Lucas_Using_Inv( i , K ) ) % mod ) % mod + mod ) % mod ) % mod ) % mod ; 
    }

    cout << answer << '\n' ; 
}

结尾撒花 \(\color{pink}{✿✿ヽ(°▽°)ノ✿}\)

posted @ 2024-03-21 17:02  HANGRY_Sol&Cekas  阅读(15)  评论(1编辑  收藏  举报