• 同色不相邻的方案数求解


    同色不相邻的方案数求解

    引例:

    (n)种颜色的小球,
    每种颜色的小球有(a_i)个,即一共有(sum_{i=1}^n a_i)个小球。
    现在要求把这些小球排同色不相邻的方案数求解成一行,要求同种颜色的小球不相邻
    求方案数,答案对(10^9+7)取模。 提交网址:web

    前言

    下面的算法都只考虑 同色小球之间无区别 的方案数。
    可重排列的情况。
    如果同色小球之间有区别,为不重排列,那么对应乘上 (prod (a_i!)) 即可。

    方法1:动态规划

    解法

    从前往后枚举每种颜色的小球。
    (f_{i,j})表示考虑完前(i)种小球,存在(j)违法相邻的方案数。
    转移首先考虑将(a_i)个小球分为(k)段,一共(inom{a_i-1}{k-1})种方法。
    这一共造成了(a_i - k)个新的不合法情况。
    枚举把其中(l)块插入原先的不合法位置之间,那么一共消除了(l)个不合法相邻。
    这一共有(inom{j}{l})种选择,剩下的块有(inom{sum + 1 - j}{k - l})种选择。
    所以转移方程式:

    [f_{i,j-l+(a_i-k)} = sum f_{i,j} inom{a_i-1}{k-1} inom{j}{l} inom{sum + 1 - j}{k - l} ]

    其中(sum = sum_{e=1}^{i-1} a_e)
    满复杂度:(O(n(sum a)a_{max}^2)),但是非常不满,实测跑 (n,sum aleq 1000)并不虚。
    这种方法好写好理解,但是适用性较窄(不方便添加整段权值)。

    实现代码

    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 2005
    #define mod 1000000007
    using namespace std;
    
    ll n , q, x , sum , cur;
    ll a[maxn] , fac[maxn] , c[maxn][maxn] , f[3][maxn];
    
    void Pre(){
        fac[0] = 1;
        for(int i = 1; i <= n; i ++)fac[i] = 1ll*i*fac[i-1] % mod;
        c[0][0] = 1;
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j < i; j ++)
                c[i][j] = ( c[i-1][j] + c[i-1][j-1] ) % mod;
            c[i][0] = c[i][i] = 1;
        }
        return;
    }
    
    int main(){
        cin >> n >> q;
        for(int i = 1; i <= n; i ++) a[i] = gi();
        Pre();
        sum = 0; cur = 0;
        f[cur][0] = 1;
        for(int i = 1; i <= n; i ++){
            cur ^= 1;
            for(int t = 0; t <= a[i]+sum+1; t ++)
                f[cur][t] = 0;
            for(int k = 1; k <= a[i] && k <= sum+1; k ++)
                for(int j = 0; j <= sum; j ++)
                    for(int l = 0; l <= k && l <= j; l ++){                  
                        f[cur][j-l+a[i]-k] +=
                            1ll*f[cur^1][j]*c[a[i]-1][k-1]%mod*(1ll*c[j][l]*c[sum+1-j][k-l]%mod)%mod;
                        f[cur][j-l+a[i]-l] %= mod;
                    }
            sum += a[i];
        }
        cout<<f[cur][0]; return 0;
    }
    

    方法2:容斥原理

    解法

    全部不相邻的方案数 = 全部不相临的排列数 - 1个相邻的排列数 + 2个相邻的排列数 ......
    直接枚举不好算,单独考虑一种小球的贡献。
    由于可重排列:(P = frac{(sum a)!}{ prod (a!)}),所以先只乘(frac{1}{a!}),最后乘上((sum a)!)即可。
    我们可以列出有(a_i)个小球的容斥多项式:

    [f(a_i) = sum_{i = 1} ^ {a_i} (-1)^{a_i-i} frac{1}{i!}E(a_i , i) ]

    其中(E(i,j))表示(i)个小球因为违法相邻而实际只有(j)段的方案数。
    显然就是要保留(i-1)个间隔中的(j-1)个,所以(E(i,j) = inom{i-1}{j-1})
    把每种小球的容斥多项式卷积卷起来就可以得到总的容斥式。
    对于最后的总容斥式的每一项,乘上对应的阶乘(可重排列的分子),再加起来即可。
    总时间复杂度:(O(n(sum a)a_{max}))
    这种做法较难理解、容易写错,但是复杂度低,适用性好(添加权值直接在计算系数时乘上即可)。

    实现代码

    //http://acm.hdu.edu.cn/showproblem.php?pid=4532
    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define N 505 
    using namespace std;
    const int mod = 1000000007 ; 
    int dp[10*N],C[N+5][N+5],E[N+5][N+5],fact[N+5],a[N+5],n,sum,ans;
    int inv[N+5],inv_fact[N+5],coef[N+5]; 
    
    IL void Pre(){
    	C[0][0] = 1;
    	for(RG int i = 1,j; i <= N; i ++)
    		for(j = C[i][0] = 1; j <= i; j ++)
    			C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod ;
    	for(RG int i = 1,e; i <= N; i ++)
    		for(RG int j = i,e = 1; j >= 1; j --)
    			E[i][j] = (mod + C[i-1][j-1] * e) % mod, e = -e ;
    	fact[0] = inv[0] = 1 ;
    	inv[1] = inv_fact[0] = 1 ;
    	for(RG int i = 1; i <= N; i ++){
    		fact[i] = 1ll * i * fact[i-1] % mod;
    		if(i ^ 1)inv[i] = 1ll * (mod - mod/i) * inv[mod % i] % mod;
    		inv_fact[i] = 1ll * inv_fact[i-1] * inv[i] % mod ; 
    	}return ; 
    }
    
    IL void upt(RG int &x,RG int y){x += y; if(x>=mod) x-= mod ; }
    int main(){
    	Pre() ;
    	scanf("%d",&n) ; 
    	sum = 0; dp[0] = 1 ;
    	for(RG int i = 1; i <= N; i ++) dp[i] = 0;
    	for(RG int sq = 1; sq <= n; sq ++){
    		scanf("%d",&a[sq]) ;  // 有一种小球的个数为a个.
    		for(RG int k = 1; k <= a[sq]; k ++)
    			coef[k] = 1ll * E[a[sq]][k] * inv_fact[k] % mod ;
    		//coef是 当前这个小球的容斥多项式 的每一项的系数
    		for(RG int j = sum; j >= 0; j --){
    			RG int tmp = dp[j] ;
    			dp[j] = 0;
    			for(RG int k = 0; k <= a[sq]; k ++)
    				upt(dp[j + k] , 1ll * coef[k] * tmp % mod) ; 
    		}
    		sum += a[sq];		//dp记录的是最后的总容斥式的每一项系数.
    	}
    	ans = 0;
    	for(RG int k = 0; k <= sum; k ++) upt(ans , 1ll * dp[k] * fact[k] % mod) ;
    	printf("%d
    " , ans) ;  return 0;
    }
    

  • 相关阅读:
    2020-08-11 题目题解
    N皇后问题
    逆序对(模板)
    归并选择(模板)
    快速选择(模板)
    快速排序(模板)
    vuecli4+elementui实现面包屑
    vue-路由导航(守卫)那些事
    vue-vant中ImagePreview 图片预览正确的打开方式
    Selenium 对表格table处理
  • 原文地址:https://www.cnblogs.com/GuessYCB/p/8726819.html
Copyright © 2020-2023  润新知