题意:
N个人站在一条线上传球,每个人传球次数的上限为Ai(值为1或者2),一开始每个人手上的球从左往右标为1..N,每一次传球,即是两个人手上的球互换,问传球后的所有的情况数(即最后产生的1-N的不同排列数)
范围:
N<=100W
首先通过递推就可以得到全是1的转移方程F[I]=F[I-1]+(I-1)*F[I-2]
可以发现1可以把2变成1。设1有x个,那么我们再把得出来的F[I]乘上n*(n-1)*……*(n-(n-x)+1)就可以得出答案了
#include<cstdio>
#include<algorithm>
using namespace std;
long long mod=1000000007;
long long f[1000001];
int a[1000001];
int main()
{
// freopen("316D3.in","r",stdin);
// freopen("316D3.out","w",stdout);
int n;
scanf("%d",&n);
if(n==1)
{
printf("1\n");
return 0;
}
else if(n==2)
{
printf("2\n");
return 0;
}
int i;
long long ans1=1,ans2=1;
for(i=1;i<=n-1;i++)
{
scanf("%d",&a[i]);
ans1=ans1*i%mod;
ans2=ans2*(long long)2%mod;
}
scanf("%d",&a[i]);
ans1=ans1*i%mod;
sort(a+1,a+1+n);
int s1=0,s2=0;
for(i=1;i<=n;i++)
if(a[i]==2)
break;
s1=i-1;
s2=n-s1;
if(s1!=0)
s2%=s1;
//s1-=s2;
long long ans=1;
for(i=n;i>=n-(n-s1)+1;i--)
ans=(ans*(long long)i)%mod;
f[1]=1;
f[2]=2;
for(i=3;i<=s1;i++)
f[i]=(f[i-1]+(long long)(i-1)*f[i-2])%mod;
f[0]=1;
printf("%I64d\n",f[s1]*ans%mod);
return 0;
}