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;
}


你可能感兴趣的:(bzoj2729: [HNOI2012]排队)