bzoj2729: [HNOI2012]排队
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2729
思路:简单的排列组合题
A(n,n)*(A(n+1,2)*A(n+3,m)+A(m,1)*A(2,2)*A(n+1,1)*A(n+2,m-1))
首先我们观察,男生无限制,先把男生排好即A(n,n)
然后我们排老师,老师不能相邻,n个男生有n+1个空位
如果老师被男生隔开,即有A(n+1,2)的方案
现在有了n+3个空位,还要放m个女生
即A(n+3,m)这就是式子加号前的部分
但是,在排女生前,老师可以相邻,只要我们后面用一个女生隔开
这样我们就有A(n,n)*A(2,2)*A(m,1)*A(n+2,m-1)种方案
即枚举两个老师的顺序,枚举中间的女生,最后有n+2个位置(两个老师捆在一起了)放剩下的m-1个女生
如果我们先排女生,再排老师,我们就会有很多种情况讨论
因为排完男生后,可以有两个女生相邻,可以有两组,每组两个女生相邻,还可以有三个女生相邻
最后再用两个老师把她们分开
什么,你说最后排男生?其实也可以做,只不过讨论起来比较复杂
答案很大,高精度即可
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxl=10010,P=10000; using namespace std; int n,m; struct bign{ int v[maxl],len; void del0(){while (len>1&&!v[len-1]) len--;} void clear(){memset(v,0,sizeof(v)),len=1;} bign operator *(const bign &b){ bign c;c.clear(); c.len=len+b.len; for (int i=0;i<len;i++) for (int j=0;j<b.len;j++){ c.v[i+j]+=v[i]*b.v[j]; if (c.v[i+j]>P) c.v[i+j+1]+=c.v[i+j]/P,c.v[i+j]%=P; } c.del0();return c; } bign operator +(const bign &b){ bign c;c.clear(); c.len=max(len,b.len)+1; for (int i=0;i<c.len;i++){ c.v[i]+=v[i]+b.v[i]; if (c.v[i]>P) c.v[i+1]++,c.v[i]-=P; } c.del0();return c; } void write(){ printf("%d",v[len-1]); for (int i=len-2;i>=0;i--) printf("%04d",v[i]); puts(""); } }; bign fact(int a,int b){ bign res;res.clear(); if (a>b) return res; res.v[0]=1; for (int i=a;i<=b;i++){ bign pp;pp.clear(),pp.v[0]=i; res=res*pp; } return res; } bign A(int n,int m){ if (!m){ bign res;res.clear(),res.v[0]=1; return res; } if (m>n){bign res;res.clear();return res;} return fact(n-m+1,n); } //A(n,n)*(A(n+1,2)*A(n+3,m)+A(m,1)*A(2,2)*A(n+1,1)*A(n+2,m-1)) int main(){ scanf("%d%d",&n,&m); bign ans=A(n,n)*(A(n+1,2)*A(n+3,m)+A(m,1)*A(2,2)*A(n+1,1)*A(n+2,m-1)); ans.write(); return 0; }