排队 [HNOI 2012,Codevs 1994,Bzoj 2729]
2016-05-25 20:42
281 查看
题目的地址请点击——
他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻。
那么一共有多少种排法呢?(注意:任意两个人都是不同的)
对于 30% 的数据 n≤100 , m≤100;
对于 100% 的数据 n≤2000 , m≤2000。
注意答案可能很大。
注意到男生没有限制条件,而老师的个数也只有 2,所以我们可以先计算这两种人。
因为有 n 个男生,所以一共有 n+1 个位置可以插入老师,排法有 P2n+1 种。
所以,我们要将 m 个女生插入 n+3 个空位中互相隔开,有 Pmn+3 种排法,
对于老师的第二种排法,我们需要将一个女生插入到两个老师之中,然后跟第一种情况一样的处理,有 m⋅Pm−1n+2 种排法。
综上所述,一共有 n!⋅P2n+1⋅Pmn+3+n!⋅2(n+1)⋅m⋅Pm−1n+2 种排法。
再加上高精度就可以 A 了。
#include <cstdio>
#include <cstring>
#define Max(x,y) ((x)>(y)?(x):(y))
#define LL long long
#define MOD 100000000
using namespace std;
struct bign{
int len;
LL s[1500];
bign(){
len=1;
memset(s,0,sizeof s);
s[1]=1;
}
bign operator = (const LL &num){
len=1;
s[1]=num;
return *this;
}
bign operator + (const bign&num){
bign c;c.s[1]=0;c.len=Max(num.len,len);
for(int i=1;i<=c.len;i++){
c.s[i+1]=(c.s[i]+s[i]+num.s[i])/MOD;
c.s[i]=(c.s[i]+s[i]+num.s[i])%MOD;
}
if(c.s[c.len+1])c.len++;
return c;
}
bign operator - (const bign&num){
bign c;c.s[1]=0;c.len=len;
int x=0;
for(int i=1;i<=c.len;i++){
c.s[i]=s[i]-num.s[i]+x;
x=0;
if(c.s[i]<0){
c.s[i]+=MOD;
x=-1;
}
}
while(!c.s[c.len]&&c.len>1)c.len–;
return c;
}
bign operator * (const LL&num){
bign c;c.s[1]=0;c.len=len;
for(int i=1;i<=c.len;i++){
c.s[i+1]=(c.s[i]+s[i]*num)/MOD;
c.s[i]=(c.s[i]+s[i]*num)%MOD;
}
if(c.s[c.len+1])c.len++;
return c;
}
bign operator * (const bign&num){
bign c;c.s[1]=0;c.len=len+num.len+1;
for(int i=1;i<=len;i++)
for(int j=1;j<=num.len;j++){
c.s[i+j]+=(c.s[i+j-1]+s[i]*num.s[j])/MOD;
c.s[i+j-1]=(c.s[i+j-1]+s[i]*num.s[j])%MOD;
}
while(!c.s[c.len]&&c.len>1)c.len–;
return c;
}
void out(){
for(int i=len;i>=1;i–){
if(i==len)printf(“%lld”,s[i]);
else printf(“%08lld”,s[i]);
}
}
};
LL n,m,tmp,tmp2;
bign ans1,ans2;
int main(){
scanf(”%lld%lld”,&n,&m);tmp=n+4;tmp2=n+3;
for(LL i=1;i<=n;i++){
ans1=ans1*i;
ans2=ans2*i;
}
ans1=ans1*(n+1)*n;
for(LL i=1;i<=m&&tmp>=0;i++){
tmp–;
ans1=ans1*tmp;
}
for(LL i=1;i<=m-1&&tmp2>=0;i++){
tmp2–;
ans2=ans2*tmp2;
}
ans2=ans2*2*m*(n+1);
(ans1+ans2).out();
return 0;
}
排队
【题目描述】
某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻。
那么一共有多少种排法呢?(注意:任意两个人都是不同的)
【输入】
只有一行且为用空格隔开的两个非负整数 n 和 m,其含义如上所述。对于 30% 的数据 n≤100 , m≤100;
对于 100% 的数据 n≤2000 , m≤2000。
【输出】
仅包含一个非负整数,表示不同的排法个数。注意答案可能很大。
【样例输入】
1 1【样例输出】
12【Solution】
这道题目我们可以分步进行计算。注意到男生没有限制条件,而老师的个数也只有 2,所以我们可以先计算这两种人。
第一步,排男生。
因为一共有 n 个男生,所以有 n! 种排法。第二步,排老师。
这个时候,就有两种情况了。第一种,我们把两个老师在这一步就分开放。
将 2 个老师插入到已经排好的男生中。因为有 n 个男生,所以一共有 n+1 个位置可以插入老师,排法有 P2n+1 种。
第二种,我们把两个老师排在一起。
把两个老师看成一个老师,这个新的老师有两种情况(因为两个老师可以顺序不同),然后再把这个新的老师插入到男生当中,排法有 2(n+1) 种。第三步,排女同学。
对于老师的第一种排法,我们已经不需要考虑老师和男同学的区别了,因为老师的限制条件已经被解决了。所以,我们要将 m 个女生插入 n+3 个空位中互相隔开,有 Pmn+3 种排法,
对于老师的第二种排法,我们需要将一个女生插入到两个老师之中,然后跟第一种情况一样的处理,有 m⋅Pm−1n+2 种排法。
综上所述,一共有 n!⋅P2n+1⋅Pmn+3+n!⋅2(n+1)⋅m⋅Pm−1n+2 种排法。
再加上高精度就可以 A 了。
Code
[cpp] #include <iostream>#include <cstdio>
#include <cstring>
#define Max(x,y) ((x)>(y)?(x):(y))
#define LL long long
#define MOD 100000000
using namespace std;
struct bign{
int len;
LL s[1500];
bign(){
len=1;
memset(s,0,sizeof s);
s[1]=1;
}
bign operator = (const LL &num){
len=1;
s[1]=num;
return *this;
}
bign operator + (const bign&num){
bign c;c.s[1]=0;c.len=Max(num.len,len);
for(int i=1;i<=c.len;i++){
c.s[i+1]=(c.s[i]+s[i]+num.s[i])/MOD;
c.s[i]=(c.s[i]+s[i]+num.s[i])%MOD;
}
if(c.s[c.len+1])c.len++;
return c;
}
bign operator - (const bign&num){
bign c;c.s[1]=0;c.len=len;
int x=0;
for(int i=1;i<=c.len;i++){
c.s[i]=s[i]-num.s[i]+x;
x=0;
if(c.s[i]<0){
c.s[i]+=MOD;
x=-1;
}
}
while(!c.s[c.len]&&c.len>1)c.len–;
return c;
}
bign operator * (const LL&num){
bign c;c.s[1]=0;c.len=len;
for(int i=1;i<=c.len;i++){
c.s[i+1]=(c.s[i]+s[i]*num)/MOD;
c.s[i]=(c.s[i]+s[i]*num)%MOD;
}
if(c.s[c.len+1])c.len++;
return c;
}
bign operator * (const bign&num){
bign c;c.s[1]=0;c.len=len+num.len+1;
for(int i=1;i<=len;i++)
for(int j=1;j<=num.len;j++){
c.s[i+j]+=(c.s[i+j-1]+s[i]*num.s[j])/MOD;
c.s[i+j-1]=(c.s[i+j-1]+s[i]*num.s[j])%MOD;
}
while(!c.s[c.len]&&c.len>1)c.len–;
return c;
}
void out(){
for(int i=len;i>=1;i–){
if(i==len)printf(“%lld”,s[i]);
else printf(“%08lld”,s[i]);
}
}
};
LL n,m,tmp,tmp2;
bign ans1,ans2;
int main(){
scanf(”%lld%lld”,&n,&m);tmp=n+4;tmp2=n+3;
for(LL i=1;i<=n;i++){
ans1=ans1*i;
ans2=ans2*i;
}
ans1=ans1*(n+1)*n;
for(LL i=1;i<=m&&tmp>=0;i++){
tmp–;
ans1=ans1*tmp;
}
for(LL i=1;i<=m-1&&tmp2>=0;i++){
tmp2–;
ans2=ans2*tmp2;
}
ans2=ans2*2*m*(n+1);
(ans1+ans2).out();
return 0;
}
相关文章推荐
- sql中drop、truncate和delete的区别
- C#中的String,StringBuilder 常用方法总结
- python switch 实现
- [RxJS] Combination operators: concat, startWith
- Xcode自动填充方法插件:AMMethod2Implement
- android studio 从Git拉工程 前期配置
- 51NOD 1003 阶乘后面0的数量(一个思维的题目)
- 经典问题:求是否存在前缀和(1671)
- opencv在visual studio的配置技巧
- Ecshop安装过程中的的问题:cls_image::gd_version()和不支持JPEG
- 圆形ImageView
- Nginx 配置详解
- python之os模块
- 漂亮的代码3:flatten 一个数组
- 杭电2844
- 多点触控
- C++第六次作业
- 数据仓库---企业信息工厂(EIF)
- Activity四种启动模式(standard,singleTop,singleTask,singleInstance)
- LeetCode题解——Palindrome Pairs