[快速幂][数学][dp] Jzoj P5813 计算

Description

 

Input

一行由空格隔开的两个整数,分别是 n 和 m。

Output

一行表示答案。 
 

Sample Input

Input1:
6 1
Input2:
6 3

Sample Output

Output1:
10
Output2:
2248
 

Data Constraint

 

Hint

第一个样例中,合法的方案有 (1, 1),(1, 2),(1, 3),(1, 6),(2, 1),(2, 2),(2, 3),(3, 1),(3, 2),(6, 1) 共 10 种。 

 

 

题解

  • 令F(x)=∏(1<=i<=2m)xi,如果F(x)<n^m
  • 令F(x')=∏(1<=i<=2m)n/xi,则F(x')>n^m
  • 因为,F(x)*F(x')==n^2m,F(x)<n^m,所以F(x')>n^m
  • 那么发现一个F(x)一定只对应一个F(x')
  • 设F(x)<n^m有s1个,F(X)=n^m有s2个,F(X)>n^m有s3个
  • 那么s1+s2+s3=为最后答案,那么s1=s3,所以只用求出s2的个数就好了
  • 就转换为求F(X)=n^m的个数
  • 现将n分解质因数n=p1^c1+p2^c2+...pk^ck
  • n^m分解质因数就是p1^(c1*m)+p2^(c2*m)+...pk^(ck*m)
  • 考虑就出对于每一个质因数的选数方案,再将它们乘起来就是所有的方案数了
  • 考虑用dp求出选的方案数,设f[i][j]为选了i个数,sum是j的方案数
  • f[i][j]=f[i][j]+f[i-1][j-k]
  • j为枚举的当前的sum值0<=j<=ci*m
  • k为枚举的转移的值0<=k<=min(ci,j)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cmath>
 6 using namespace std;
 7 long long s2,f[210][210*100],mo=998244353,tot;
 8 int n,m,k,num;
 9 long long ksm(long long a,int b)
10 {
11     long long r=1;
12     for(;b;b>>=1,a=a*a%mo) if(b&1) r=r*a%mo;
13     return r;   
14 }
15 void dp(int x)
16 {
17     memset(f,0,sizeof(f)); f[0][0]=1;
18     num=0; while (k%x==0) num++,k=k/x;
19     for (int i=1;i<=m*2;i++)
20         for (int j=0;j<=num*m;j++)
21             for (int z=0;z<=min(j,num);z++)
22                 f[i][j]=(f[i][j]+f[i-1][j-z])%mo;
23     s2=(s2*f[m*2][num*m])%mo;
24 }
25 int main()
26 {
27     freopen("count.in","r",stdin);
28     freopen("count.out","w",stdout);
29     scanf("%d%d",&n,&m);
30     k=n; s2=1;
31     for (int i=1;i<=sqrt(n);i++)
32         if (n%i==0)
33         {
34             tot++;
35             if (i*i<n) tot++;
36             if (i>1&&k%i==0) dp(i);
37         }
38     if (k>1) dp(k);
39     printf("%lld",(ksm(tot,2*m)+s2)%mo*ksm(2,mo-2)%mo);
40     return 0;
41 }

 

posted @ 2018-08-14 16:25  BEYang_Z  阅读(203)  评论(0编辑  收藏  举报