[数论][DP]JZOJ 5813 计数

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 种。 

分析

以下讨论的xi全部满足第一个限制条件

对于一组{x1,x2,..,x2m}<nm,显然:{n/x1,n/x2,..n/x2m}>nm

那么则两式相乘则为n2m,然后xi与n/xi显然一一对应,所以设上式为A和C,有A=C(方案数)

那么显然A+B(=nm的方案数)+C=σ(n)2m

所以A=(σ(n)2m+B)/2

那么就只需要求出=n2m的情况就行了,先分解质因数,设f[i][j]表示选了i位,和为j的方案数,DP即可

#include <istream>
#include <cstdio>
#include <memory.h>
#include <cmath>
using namespace std;
typedef long long ll;
const ll P=998244353;
ll n,m,k,B;
ll cnt,f[201][20001];

ll Power(ll x,ll y) {
    ll ans=1;
    while (y) {
        if (y&1) (ans*=x)%=P;
        (x*=x)%=P;
        y>>=1;
    }
    return ans;
}

void Solve(int l) {
    memset(f,0,sizeof f);f[0][0]=1;
    int ncnt=0;
    while (!(k%l)) k/=l,ncnt++;
    for (int i=1;i<=2*m;i++)
    for (int j=0;j<=ncnt*m;j++)
    for (int k=0;k<=min(j,ncnt);k++)
    (f[i][j]+=f[i-1][j-k])%=P;
    (B*=f[2*m][ncnt*m])%=P;
}

int main() {
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    k=n;B=1;
    for (int i=1;i<=sqrt(n);i++)
    if (!(n%i)) {
        cnt++;
        if (i*i<n) cnt++;
        if (i-1&&!(k%i)) Solve(i);
    }
    if (k>1) Solve(k);
    printf("%lld",(Power(cnt,2*m)+B)%P*Power(2,P-2)%P);
    fclose(stdin);fclose(stdout);
}
View Code

 

posted @ 2018-08-15 07:44  Vagari  阅读(188)  评论(0编辑  收藏  举报