zoukankan      html  css  js  c++  java
  • 计数

    Time Limit: 1000 ms Memory Limit: 256 MB

    description

    img


    吐槽

    所以说。。组合数的题是不是都是知道大致思路但是就是不会写qwq菜醒qwq

    正题

    这题其实感觉有点玄妙啊,自己想的话总是会想复杂。。但其实情况还是很好考虑的重点在于怎么枚举

    首先讲一下大致思路

    总共有4中不同的字符,相邻两个不能相同,那么我们可以考虑两种字符的排列(也就是先考虑(A)(B)怎么放和(C)(D)怎么放),然后再把(AB)的放法和(CD)的放法用插空的方式组合起来求得最后的答案

    因为处理起来其实是一样的所以这里就只写(AB)的情况了

    AB的情况

    (A)(B)有四种最基本的满足要求的摆放:

    (1)(A) (2)(B) (3)(AB) (4)(BA)

    我们枚举这些字符总共组成了多少个区间(这里的区间指的是在最后合并插空之后这段字符还是连在一起的,也就是说没有(C)(D)插在中间),设总共有(i)个区间

    我们再枚举一下(1)的个数,这时会发现每多一个(1)类区间,(A)的总数就会比(B)多一个,每多一个(2)类区间(A)就会比(B)少一个,而(3)和(4)的话不会对差有任何影响。

    由于(A)的个数和(B)的个数是固定的,也就是说(A)(B)的差是固定的,(1)的个数一旦确定,为了保证(A)(B)的差满足条件,(2)的个数也就确定下来了。

    我们记(1)类区间的个数为(a),那么(2)类区间的个数就是(b = n1-n2+a)

    接着再看(3)和(4),会发现其实这两类本质上是一样的,我们只用枚举一类然后乘上组合数(其实就是(2^i),因为每一位可以是(A)或者(B)),这样我们就可以把这两类看做一类了,这剩下的一类的区间总数就是(c=i - a -b)

    接下来看怎么算排列

    首先对于(1)类和(2)类,我们用掉(a)(A)(b)(B),排列的方案数显然是$C_{i}^{a} $ * (C_{i-a}^{b})

    对于剩下的(3)和(4),我们还有(n1+n2-a-b)个字符可以使用,也就是总共有(d=frac{n1+n2-a-b}{2})(AB)

    (d)对字符首先要放进(c)个区间中(那么显然这里就要求(d>=c)了,在枚举的时候要注意范围),如果还有剩余,再将剩余的(d-c)对分配到(i)个区间中,那么方案就是(C_{i+d-c+1}^{i-1})
    为什么是这个东西嘞?
    其实问题就相当于求(i)个非负数之和=(d-c)的方案数,我们先在等式两边都加上(i),然后变成(i)个正整数之和(=i+d-c),然后用隔板法就好了(枚举分割线在哪个空隙)。
    这样操作是因为隔板法的使用前提是不能有空组(否则分割线的数量不一定是分成的份数-1),我们考虑将(i+d-c)分成i个数之后,每个数减去(1),得到i个非负整数(可以为0),此时这(i)个非负整数的和就是(d-c)了,也就是说这就是我们要求的其中一种方案,其他的情况同理,一一对应,所以这两个问题在组合数中是等价的

    我们用(f1)来表示组成(i)个区间的方案数,那么就可以得到:

    (f1_i =sumlimits_{a=0}^{n1+n2}C_{i}^{a}×C_{i-a}^{b}×2^c×C_{i+d-c+1}^{i-1})

    用同样的方式来算(C)(D)的方案,存到(f2)里面去

    接下来看插空

    插空有三种插法,下面用(AB)表示(A)(B)组成的一个区间,(CD)表示(C)(D)组成的一个区间,三种方法就可以这样表示:

    1.(AB)(CD)(AB)(CD)(AB)

    2.(CD)(AB)(CD)(AB)(CD)

    3.(AB)(CD)(AB)(CD)(AB)(CD)(或者(AB)和(CD)的顺序反过来,所以算的时候要×2)

    (其实会发现就是之前分别算(AB)区间和(CD)区间的三大类)

    那么(ans)应该就是

    [ans=sumlimits_{i=1}^{n1+n2} f1_i*f2_{i-1}+f1_{i}*f2_{i+1}+f1_{i}*f2_{i}*2 ]

    然后就十分愉快滴做完啦ovo

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define MOD 1000000007
    using namespace std;
    const int MAXN=4010;
    ll f1[MAXN],f2[MAXN],two[MAXN],C[MAXN][MAXN];
    ll ans;
    int n1,n2,n3,n4;
    int get_c(int n);
    int get_f(int num1,int num2,ll *f);
    ll calc(int c,int d);
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d%d%d%d",&n1,&n2,&n3,&n4);
    	two[0]=1;
    	int sum=n1+n2+n3+n4;
    	for (int i=1;i<=sum;++i) two[i]=(two[i-1]<<1)%MOD;
    	get_c(sum);
    	get_f(n1,n2,f1);
    	get_f(n3,n4,f2);
    	for (int i=1;i<=n1+n2;++i)
    		ans=(ans+f1[i]*f2[i-1]%MOD+f1[i]*f2[i+1]%MOD+2LL*f1[i]*f2[i]%MOD)%MOD;
    	printf("%lld
    ",ans);
    }
    
    int get_c(int n){
    	for (int i=0;i<=n;++i){
    		C[i][0]=1; C[i][i]=1;
    		for (int j=1;j<i;++j)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    	}	
    }
    
    int get_f(int num1,int num2,ll *f){
    	int a,b,c,d,sum=num1+num2;
    	for (int i=0;i<=num1+num2;++i){
    		for (a=0;a<=i;++a){//枚举(1)
    			b=num1-num2+a;//(2)
    			c=i-a-b;//(3)+(4)要填区间
    			d=(sum-a-b)/2;//(3)+(4)可用对
    			if (b<0||c<0||d<0||d<c) continue;
    			f[i]=(f[i]+C[i][a]*C[i-a][b]%MOD*two[c]%MOD*calc(i,d-c)%MOD)%MOD;
    		}	
    	}
    }
    
    ll calc(int c,int d){//c个非负整数之和=d,枚举所有区间填了一个之后剩下的放哪里
    	if (!c) return d==0;
    	return C[c+d-1][c-1];
    }
    
  • 相关阅读:
    Input标签与图片按钮水平对齐解决方法
    下载网页流
    GTD
    Eclipse 运行多个Tomcat实例
    Tomcat启动超时
    Javascript:谈谈JS的全局变量跟局部变量
    影响一生的习惯
    OpenSUSE 安装并启动Tomcat
    ExtJS实战 01——HelloWorld
    eclipse快捷键
  • 原文地址:https://www.cnblogs.com/yoyoball/p/8268966.html
Copyright © 2011-2022 走看看