源代码: #include<cstdio> #include<algorithm> #define LL long long //这坑爹的数据逼我的! #define INF 1000000007 using namespace std; LL n,m,Num(0),Sum(0),W[100001],f[100001],Prime[100001]; bool Vis[100001]={0}; struct Node { LL S,L; }i[100001]; void Euler() //欧拉筛法。 { for (LL a=2;a<=n;a++) { if (!Vis[a]) Prime[++Sum]=a; for (LL b=1;b<=Sum&&Prime[b]*a<=n;b++) { Vis[Prime[b]*a]=true; if (!(a%Prime[b])) break; } } } LL Count(LL X,LL T) //快速幂。 { LL Ans=1; while (T) { if (T&1) Ans=(Ans*X)%INF; X=(X*X)%INF; T>>=1; } return Ans; } int main() //这个数论题你出得好,而且多了一些细节在里面,整惨了。 { scanf("%d%d",&n,&m); Euler(); for (LL a=1;a<=m;a++) scanf("%d",&W[a]); sort(W+1,W+m+1); if (W[1]>1) //头。 { i[++Num].L=W[1]-1; //长度。 i[Num].S=1; //方案数。 } if (W[m]<n) //尾。 { i[++Num].L=n-W[m]; i[Num].S=1; } for (LL a=2;a<=m;a++) //区间预处理。 if (W[a]-W[a-1]-1) { i[++Num].L=W[a]-W[a-1]-1; i[Num].S=Count(2,i[Num].L-1); } for (LL a=1;a<=Sum;a++) //阶乘分解质因数。 { LL t=n-m; while (t) { f[a]+=t/Prime[a]; //这个处理就妙了,详见下。 t/=Prime[a]; } } for (LL a=1;a<=Num;a++) for (LL b=1;b<=Sum;b++) { LL t=i[a].L; while (t) { f[b]-=t/Prime[b]; t/=Prime[b]; } } LL Ans=1; for (LL a=1;a<=Sum;a++) for (LL b=1;b<=f[a];b++) Ans=(Ans*Prime[a])%INF; //最后统计。 for (LL a=1;a<=Num;a++) Ans=(Ans*i[a].S)%INF; printf("%lld",Ans); return 0; } /* 什么叫做神题,这***的就叫神题! 分析问题,如样例3:□ □ □ √ □ □ □ √ □ □ □ 将已点燃的灯作为割点进行分段,会发现,单独的一段方案数为2^(Length-1)。 那么怎样将其合并到一起呢? 1 1 1 2 2 2 3 3 3,其中的数字表示这一次在第几段中取数,那么大的框架就形成了: (n-m)!/p1!p2!...pq! 这个公式如此牛*,怎么来的呢? (n-m)!表示这些数总的全排列,仔细想一想,除以这些数就可以得到对于此区间数单一的方案总数。 但这只是针对一种方案的排列总数,接下来再乘以每段的方案数即可。 直接处理会导致溢出或错误,需要进行质因数分解,因为是阶乘,所以分解比较特别。
当然,阶乘分解质因数也是一个噱头。
对于n!=1*2*3*4*...*(n-2)*(n-1)*n,其中m的倍数有:m、2m、3m、...、n/m*m(共(n/m)个),则先加上(n/m)。
处理之后,数列就变为了:1、2、3、...、n/m/m,其中m的倍数有:m、2m、3m、...、n/m/m*m(共(n/m/m)个),则再加上(n/m/m),以此类推,直到为空。 */