bzoj 2729: [HNOI2012]排队
Description
某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)
Solution
写复杂了
可以先摆好 \(n\) 个男同学,再把女同学和老师分别插空,保证不相邻
发现女生实际是可以先相邻然后再插老师的,所以讨论老师的摆法
设老师为\(T\),女生为\(G\),有 \(GTG\),\(GTGTG\) 两种,分别把这个整体当作女生计算一下即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4010,mod=1e5;
int n,m,prime[N],pre[N],num=0,cnt[N];
void priwork(){
for(int i=2;i<=n+m+2;i++){
if(!pre[i])prime[++num]=i,pre[i]=i;
for(int j=1;j<=num && i*prime[j]<=n+m+2;j++){
pre[i*prime[j]]=prime[j];
if(i%prime[j]==0)break;
}
}
}
inline void add(int x){
if(!x)cnt[0]++;
while(x>1){
cnt[pre[x]]++;
x/=pre[x];
}
}
struct sub{
int len,a[N];
sub(){memset(a,0,sizeof(a));len=0;}
inline void init(){len=0;memset(a,0,sizeof(a));a[++len]=1;}
inline void operator *=(const int &t){
for(int i=1;i<=len;i++)a[i]*=t;
for(int i=1;i<=len;i++)
if(a[i]>=mod)a[i+1]+=a[i]/mod,a[i]%=mod;
while(a[len+1]){
len++;
if(a[len]>=mod)a[len+1]+=a[len]/mod,a[len]%=mod;
}
}
inline void operator +=(const sub &t){
len=max(len,t.len);
for(int i=1;i<=len;i++){
a[i]+=t.a[i];
if(a[i]>=mod)a[i]-=mod,a[i+1]++;
}
while(a[len+1]){
len++;
if(a[len]>=mod)a[len+1]++,a[len]-=mod;
}
}
inline void Print(){
printf("%d",a[len]);
for(int i=len-1;i>=1;i--)
for(int j=mod/10;j;j/=10)
printf("%d",a[i]/j),a[i]%=j;
puts("");
}
};
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
cin>>n>>m;
priwork();
for(int i=n-m+2;i<=n+1;i++)add(i);
for(int i=1;i<=n;i++)add(i);
add(n+m);add(n+m+1);
sub ans,t;
t.init();
for(int i=0;i<=n+m+2;i++)
for(int j=1;j<=cnt[i];j++)t*=i;
ans+=t;
memset(cnt,0,sizeof(cnt));t.init();
for(int i=1;i<=n;i++)add(i);
for(int i=n-m+3;i<=n+1;i++)add(i);
add(m);add(m-1);add(n+m);add(2);
for(int i=0;i<=n+m+2;i++)
for(int j=1;j<=cnt[i];j++)t*=i;
ans+=t;
memset(cnt,0,sizeof(cnt));t.init();
for(int i=1;i<=n;i++)add(i);
for(int i=n-m+4;i<=n+1;i++)add(i);
add(m);add(m-1);add(m-2);add(m-1);
for(int i=0;i<=n+m+2;i++)
for(int j=1;j<=cnt[i];j++)
t*=i;
ans+=t;
ans.Print();
return 0;
}