[HNOI2012]排队 组合数

公式:A(n,n)*A(n+1,2)*A(n+3,m) +    A(n,n)*C(m,1)*A(2,2)*C(n+1,1)*A(n+2,m-1)
分情况讨论推出公式
前者为无论何时都合法的,后者为先不合法,然后再合法的(两个老师先站在一 起,然后一个女生
插进来,所以要把这3个人看成一个整体,然后老师可以左右换,所以乘2,女生    就是m选1,然后整体再插入队伍
最后得到上面的式子
由于答案较大,所以要写高精
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define AC 10000
  4 #define R register int
  5 #define D printf("the k is %d\n",k);
  6 /*A(n,n)*A(n+1,2)*A(n+3,m) +    A(n,n)*C(m,1)*A(2,2)*C(n+1,1)*A(n+2,m-1)
  7 分情况讨论推出公式
  8 前者为无论何时都合法的,后者为先不合法,然后再合法的(两个老师先站在一 起,然后一个女生
  9 插进来,所以要把这3个人看成一个整体,然后老师可以左右换,所以乘2,女生    就是m选1,然后整体再插入队伍
 10 最后得到上面的式子
 11 */
 12 int sum2[AC],a[AC],sum1[AC],k,n,m,tot,tot1,tot2,ans[AC];//每次计   算前将memset a,将高精sum*低精k存入a,然后计算完毕后将a导入sum
 13 
 14 void ADD()//加法
 15 {
 16     for(R i=1;i<=max(tot1,tot2);i++)//error!!!注意这里要加到max(tot1,tot2)!!!
 17     {
 18         ans[i+1]=ans[i+1]+(sum1[i]+sum2[i]+ans[i])/10000;
 19         ans[i]=(ans[i]+sum1[i]+sum2[i])%10000;
 20     }
 21     tot=(tot1+tot2)%10000;
 22     while(!ans[tot] && tot>1)--tot;//注意边界!!!
 23 }
 24 
 25 void add1()//乘法1,计算前一部分
 26 {
 27     memset(a,0,sizeof(a));
 28     for(R i=1;i<=tot1;i++)
 29     {
 30         a[i+1]=a[i+1]+(a[i]+sum1[i]*k)/10000;
 31         a[i]=(a[i]+sum1[i]*k)%10000;
 32     }
 33     tot1+=1;//因为n和m最大就是2000,所以最多也就加这么多位了,又因为压了位,,,所以
 34     while(!a[tot1] && tot1>1)--tot1;
 35     if(!a[tot1])memset(sum1,0,sizeof(sum1));//如果女生太多了会导致没有合法情况!!
 36     else    for(R i=1;i<=tot1;i++)   sum1[i]=a[i];//导入sum
 37 }
 38 
 39 void add2()//乘法2,计算后一部分
 40 {
 41     memset(a,0,sizeof(a));
 42     for(R i=1;i<=tot2;i++)
 43     {
 44         a[i+1]=a[i+1]+(a[i]+sum2[i]*k)/10000;
 45         a[i]=(a[i]+sum2[i]*k)%10000;
 46     }
 47     tot2+=1;//因为n和m最大就是2000,所以最多也就加这么多位了,又因为压了位,,,所以
 48     while(!a[tot2] && tot2>1)--tot2;
 49     if(!a[tot2])memset(sum2,0,sizeof(sum2));
 50     else for(R i=1;i<=tot2;i++)  sum2[i]=a[i];//导入sum
 51 }
 52 
 53 void work()
 54 {
 55     sum1[1]=1;//初始值
 56     tot1=1;
 57     for(R i=2;i<=n;i++)//计算A(n,n),即n!
 58     {
 59         k=i;
 60         add1();
 61     }
 62     k=n;
 63     add1();
 64     k=n+1;//分开乘,不然k太大会爆
 65     add1();
 66     k=n+3;
 67     for(R i=1;i<=m;i++)//计算A(n+3,m)
 68     {
 69         add1();
 70         --k;
 71     }
 72     //一二部分分割线~~~~~~~~~~~~~~~
 73     sum2[1]=2*(n+1);//不会破万,所以直接放在第一个即可
 74     tot2=1;
 75     k=m;
 76     add2();
 77     for(R i=2;i<=n;i++)
 78     {
 79         k=i;
 80         add2();
 81     }
 82     k=n+2;
 83     for(R i=1;i<=m-1;i++)
 84     {
 85         add2();
 86         --k;
 87     }
 88     ADD();//最后把sum1和sum2相加得到ans
 89     printf("%d",ans[tot--]);
 90     for(R i=tot;i>=1;i--)printf("%04d",ans[i]);
 91     /*printf("\n");
 92     while(!sum1[tot1])tot1--;
 93     printf("%d",sum1[tot1]);
 94     for(R i=tot1-1;i>=1;i--)printf("%04d",sum1[i]);*/
 95 }
 96 
 97 int main()
 98 {
 99     freopen("in.in","r",stdin);
100     scanf("%d%d",&n,&m);
101     work();
102     fclose(stdin);
103     return 0;
104 }

 

 
posted @ 2018-04-09 21:23  ww3113306  阅读(257)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。