点亮灯笼

源代码:

#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),以此类推,直到为空。
*/
posted @ 2016-10-24 14:10  前前前世。  阅读(208)  评论(0编辑  收藏  举报