[HNOI2012] 排队
题目描述
某中学有 n n n 名男同学, m m m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)
输入格式
只有一行且为用空格隔开的两个非负整数 n n n 和 m m m,其含义如上所述。
输出格式
仅一个非负整数,表示不同的排法个数。注意答案可能很大。
样例 #1
样例输入 #1
1 1
样例输出 #1
12
提示
对于 30 % 30\% 30% 的数据 n ≤ 100 n\leq 100 n≤100, m ≤ 100 m\leq 100 m≤100。
对于 100 % 100\% 100% 的数据 n ≤ 2000 n\leq 2000 n≤2000, m ≤ 2000 m\leq 2000 m≤2000。
思路
- 首先,这道题我们可以分成两个条件(因为直接算很难),条件1:任意两名女同学不能相邻。条件2:两名老师也不能相邻。
- 此时,用到高中容斥原理,要让条件1和条件2同时成立,我们可以先让条件1成立得到的答案设为 a n s ans ans,然后把 a n s ans ans减去条件1成立,条件2成立(设为 r e s res res)。(这样很好做,我高中的时候经常这么玩)
- 然后对于 a n s ans ans的计算,我们可以很快的算出: A n + 3 m × A n + 2 n + 2 A_{n+3}^{m} \times A_{n+2}^{n+2} An+3m×An+2n+2。
- r e s res res的计算,可以是: 2 × A n + 1 n + 1 × A n + 2 m 2\times A_{n+1}^{n+1}\times A_{n+2}^{m} 2×An+1n+1×An+2m
- 答案为二者之差。
- 注意:
r
e
s
res
res中的
m
≤
n
+
3
m\le n+3
m≤n+3并且
a
n
s
ans
ans中的
m
≤
n
+
2
m\le n+2
m≤n+2。(不这样可能会出错)
但注意,本道题得使用高精度x高精度的乘法。
先附上模板:
vector<int> mul(vector<int> A, vector<int> B) {
vector<int>C(A.size()+B.size()+100,0);
int t = 0;
for(int i=0;i<A.size();i++) {
for(int j=0;j<B.size();j++) {
C[i+j]+=A[i]*B[j];
}
}
for(int i=0;i<C.size();i++) {
t+=C[i];
C[i]=t%10;
t/=10;
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
那么这样这道题就很好做了。
代码
//高精度+组合数学
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,m;
vector<int> add(vector<int> a,vector<int> b){
int t=0;
vector<int>res;
for(int i=0;i<a.size()||i<b.size();i++){
if(i<a.size())t+=a[i];
if(i<b.size())t+=b[i];
res.push_back(t%10);
t/=10;
}
if(t)res.push_back(1);
return res;
}
vector<int> sub(vector<int> a,vector<int> b){
int t=0;
vector<int>res;
for(int i=0;i<a.size();i++){
t=a[i]-t;
if(i<b.size())t-=b[i];
res.push_back((t+10)%10);
if(t<0)t=1;
else t=0;
}
while(res.size()>1&&res.back()==0)res.pop_back();
return res;
}
//高精度x低精度
vector<int> mul(vector<int> a,int b){
int t=0;
vector<int>res;
for(int i=0;i<a.size()||t;i++){
if(i<a.size())t+=a[i]*b;
res.push_back(t%10);
t/=10;
}
return res;
}
//高精度x高精度
vector<int> mul(vector<int> A, vector<int> B) {
vector<int>C(A.size()+B.size()+100,0);
int t = 0;
for(int i=0;i<A.size();i++) {
for(int j=0;j<B.size();j++) {
C[i+j]+=A[i]*B[j];
}
}
for(int i=0;i<C.size();i++) {
t+=C[i];
C[i]=t%10;
t/=10;
}
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
vector<int> A(int a,int b){
vector<int> k;
k.push_back(1);
for(int i=a,j=b;i>=1;i--,j--){
k=mul(k,j);
}
return k;
}
vector<int> a,b,c,d,e,f,g;
int main(){
cin>>n>>m;
if(n+3>=m){//这两个限制条件
a=A(m,n+3);
b=A(n+2,n+2);
c=mul(a,b);
g=add(g,c);//g+=c;
}
if(n+2>=m){
d=mul(A(n+1,n+1),2);
e=A(m,n+2);
f=mul(d,e);
g=sub(g,f);//g-=f;
}
for(int i=g.size()-1;i>=0;i--){
cout<<g[i];
}
return 0;
}