依旧不会。。看别人的题解。。我组合数学实在是太弱了
题意 n个灯,有k盏亮的,每次点亮的只能是已经亮的旁边的灯。问有多少种方法全部点亮
如果没有限制,那么是n! 加上限制,需要除以 连续灭灯区间的个数阶乘,再乘以电量方法。比如 00010001000 9!/3!/3!/3!*2^(3-1) 左右两边只有一种点亮方法。两盏亮的灯之间有2^(numb-1)种方法。因为最后一盏灯无选择了。
至于n!因为n的范围是1000,所以先存在数组里s[i]=i,对每个需要除的阶乘去分解s[]。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
int n,k;
long long pos,it[1111],light[1111];
long long s[1111];
long long gcd(long long a,long long b){
return b==0?a:gcd(b,a%b);
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
pos=0;
long long sum=0;
it[0]=0;it[k+1]=n+1;
for(int i=1;i<=k;i++){
scanf("%d",&it[i]);
}
sort(it+1,it+1+k);
for(int i=1;i<=k+1;i++){//printf("!! %d\n",it[i]);
if(it[i]-it[i-1]>2)
light[pos++]=it[i]-it[i-1]-1;
}
for(int i=2;i<=k;i++){
if(it[i]-it[i-1]>2)sum+=(it[i]-it[i-1]-2);
}
// printf("!!sum=%d\n",sum);
long long numb=n-k;
for(long long i=1;i<=numb;i++){
s[i]=i;
}
for(int i=0;i<pos;i++){//printf("!!light=%d\n",light[i]);
for(int j=2;j<=light[i];j++){ //拿出阶乘里的一个数,去分解s[]
long long jj=j;
for(int ii=2;ii<=numb;ii++){
long long find=gcd(s[ii],jj);
s[ii]/=find;
jj/=find;
if(jj==1){
break;
}
}//printf("!! jj=%d\n",jj);
}
}
long long sum1=1;
for(int i=2;i<=numb;i++){
sum1=(sum1*s[i])%1000000007LL;
}// printf("%d\n",sum1);
for(int i=1;i<=sum;i++){
sum1=(sum1*2)%1000000007LL;
}
printf("%d\n",sum1);
}
return 0;
}
/*
74 13
6 14 19 20 21 24 30 43 58 61 69 70 73
*/