HDU4675【GCD of scequence】【组合数学、费马小定理、取模】

看题解一开始还有地方不理解,果然是我的组合数学思维比较差哭

然后理解了之后自己敲了一个果断TLE。。。。哭

我以后果然还得多练啊哭

好巧妙的思路啊哭

 

知识1:

费马小定理是数论中的一个重要定理,其内容为:假如p是质数,且(a,p)=1,那么 a^(p-1) ≡1mod p假如p是质数,且a,p互质,那么 a(p-1)次方除以p的余数恒等于1

对于除法取模还需要用到费马小定理: a ^ (p - 1) % p = 1; -> a ^ (p - 2) % p = (1 / a) % p;

 

巧妙1:

for(int i=1;i<=n;i++)

{ int temp; scanf("%d",&temp); sum1[temp]++; }

 

for(int j=i;j<=m;j+=i) sum+=sum1[j];

直接判断倍数是否有无。奋斗ORZ!!!

用这一块代码,这样再从1遍历到m的时候,速度增加非常快,然后就不会超时。

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <cstring>

#include <cmath>

#include <ctime>

#include <iostream>

#include <algorithm>

#include <string>

#include <queue>

#include <set>

#include <map>

#include <vector>

#include <assert.h>



using namespace std;



#define lowbit(i) (i&-i)

#define sqr(x) ((x)*(x))

#define enter printf("\n")

#define is_sqr(x) (x&(x-1))

#define pi acos(-1.0)

#define clr(x)  memset(x,0,sizeof(x))

#define fp1 freopen("in.txt","r",stdin)

#define fp2 freopen("out.txt","w",stdout)

#define pb push_back



typedef __int64 LL;



const double eps = 1e-7;

const double DINF = 1e100;

const int INF = 1000000006;

const LL LINF = 1000000000000000005ll;

const int MOD = (int) 1e9 + 7;

const int maxn=300005;



 template<class T> inline T Min(T a,T b){return a<b?a:b;}

 template<class T> inline T Max(T a,T b){return a>b?a:b;}

 template<class T> inline T Min(T a,T b,T c){return min(min(a, b),c);}

 template<class T> inline T Max(T a,T b,T c){return max(max(a, b),c);}





LL f[maxn],e[maxn],a[maxn],ans[maxn],sum1[maxn];

LL quick_pow(LL a,LL b)//a的b次方,快速幂取模

{

    LL ret=1;

    while(b)

    {

        if(b&1) ret=(ret*a)%MOD;

        b/=2;

        a=(a*a)%MOD;

    }

    return ret%MOD;

}

LL cal(LL n,LL k)

{

    if(k==0||n==k) return 1;

    return (f[n]*e[k]%MOD)*e[n-k]%MOD;//注意运算顺序

}



//以后某些变量还是不采用C99的写法了

int main()

{

  f[0]=e[0]=1;

  for(int i=1;i<=maxn;i++)

  {

      f[i]=f[i-1]*i%MOD;

      e[i]=quick_pow(f[i],MOD-2);

  }

  int n,m,k;

  while(scanf("%d%d%d",&n,&m,&k)!=EOF)

  {

      clr(sum1);

      for(int i=1;i<=n;i++)

      {

         int temp;

         scanf("%d",&temp);

         sum1[temp]++;

      }

      for(int i=m;i>=1;i--)//倒着写不至于每次都m/i次循环

      {

          int sum=0;

          for(int j=i;j<=m;j+=i)

            sum+=sum1[j];

          if(sum<n-k)//k个不一样的,n-k个一样的。

          {

              ans[i]=0;continue;

          }

          ans[i]=((cal(sum,n-k)*quick_pow(m/i-1,sum-(n-k))%MOD)*quick_pow(m/i,n-sum))%MOD;

          for(int j=2*i;j<=m;j+=i)

              ans[i]=(ans[i]-ans[j]+MOD)%MOD;

      }

      for(int i=1;i<m;i++) printf("%lld ",ans[i]);

      printf("%lld\n",ans[m]);

  }

  return 0;

}




 

 

你可能感兴趣的:(HDU)