【题解】广大附中2020提高组十连测(四)T3——计算

计算

(标签:动态规划、数学、玄学、毒瘤)

题目描述

给定 \(n\) ,求合法的 \((x_1,x_2,x_3, \ldots ,x_{2m})\) 组数。一组 \(x\) 是合法的,当且仅当:

  • \(\forall i \in [1,2m],\ x_i \in \mathbb{Z}^+,\ x_i|n\)
  • \(\prod\limits_{i=1}^{2m}{x_i} \leq n^m\)

合法的 \((x_1,x_2,x_3, \ldots ,x_{2m})\) 可能会有很多,请输出答案 \(mod\ 998244353\)

输入格式

一行由空格隔开的两个整数,分别是 \(n\)\(m\)

输出格式

一行表示答案。

样例

输入样例 1

6 1

输出样例 1

10

输入样例 2

6 3

输出样例 2

2248

样例解释

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

数据范围

对于 \(20\%\) 的数据 \(n \leq 50,\ m=2\)
对于 \(60\%\) 的数据 \(n \leq 100,\ m \leq 3\)
对于 \(100\%\) 的数据 \(n \leq 10^9,\ m \leq 100\)

题目分析

这道题一看大概就可以看出来是推公式,因为 \(1e9\) 的数据范围不可能是什么其它的方法
首先我们设 \(F(i)=\prod\limits_{i=1}^{2m}\ x_i,\ G(i)=\prod\limits_{i=1}^{2m}\ \frac{n}{x_i}\)
那么我们就可以开始玄学推导了,下面的内容请坚持看完别晕倒或睡着:|

---------------分----------隔----------线---------------

\[由题可知,G(x)=\frac{n^{2m}}{F(x)} \]

\[\therefore\ F(x)=\frac{n^{2m}}{G(x)} \leq n^m \]

\[\therefore\ G(x) \cdot n^m \geq n^{2m} \]

\[\therefore\ G(x) \geq n^m \]

\[又 \because\ F(x) \leq n^m \]

\[设 S_1 为满足 F(x)>n^m 的组数, S_2 为满足 F(x)=n^m 的组数, S_3 为满足 F(x)<n^m 的组数。 \]

\[又\because\ G(i)与F(i)一一对应 \]

\[\therefore\ S_1=S_3 \]

\[\therefore\ S_1+S_2+S_3=\sigma(n) \cdot m=2S_1+S_2 \]

\[S_1=\frac{\sigma(n)^m-S_2}{2} \]

\[\therefore\ ans=S_1+S_2=\frac{\sigma(n)^m+S_2}{2} \]

\(\sigma(n)\) 表示 \(n\) 的质因数个数)
那么问题就转换为计算 \(S_2\)了,也就是说,计算有多少 \(F(x)=n^m\)。这个可以使用 \(DP\) 来计算。设 \(dp_{i,j}\) 表示前 \(i\) 个数满足条件的组数是 \(j\) 的个数,可以推出状态转移方程是 \(dp_{i,j}=\sum\limits_{k=0}^{w}\ dp_{i-1,j-k}\)。其中我们发现,第 \(i\) 次的转移只和第 \(i-1\) 次有关,所以我们可以使用滚动数组;计算 \(\sum\limits_{k=0}^{w}\ dp_{i-1,j-k}\) 可以使用前缀和。
最后一个,也是最玄学的优化就是,前缀和数组和 \(DP\) 数组可以使用同一个……

完整代码

#include <bits/stdc++.h>
#define MOD 998244353
#define int long long //懒人式开long long
using namespace std;
int n,m,ans,num=1,phi=1,dp[6600];
int quickPow(int a,int b) //快速幂,不解释
{
    int res=1;
    while(b)
    {
        if(b&1)
            res=res*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return res;
}
void solve(int w) //DP过程
{
    memset(sum,0,sizeof(dp));
    dp[0]=1; //DP初始化
    for(int i=1;i<=2*m;i++)
    {
        for(int j=1;j<=w*m;j++)
            dp[j]=(dp[j-1]+dp[j])%MOD; //计算前缀和
        for(int j=w*m;j>w;j--)
            dp[j]=(dp[j]+MOD-dp[j-w-1])%MOD; //利用前缀和DP,这段代码的理解需要一定时间
    }
    phi=(phi*(w+1))%MOD; //计算n的质因数个数
    num=(num*dp[w*m])%MOD; //计算公式中的S2
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int p=2;p<=sqrt(n);p++)
    {
        if(n%p)	continue;
        int a,w=0;
        while(n%p==0)    n/=p,w++; //一些操作,以保证搜到的是质因数
        solve(w);
    }
    if(n>1)    solve(1); //n为完全平方数的特判
    ans=(quickPow(phi,2*m)+num)%MOD; //照搬公式的分子
    ans=(ans%2?(ans+MOD)/2:ans/2)%MOD; //然后除以二
    printf("%lld",你猜这里要输出什么);
    return 0;
}
posted @ 2020-07-31 15:06  ExplodingKonjac  阅读(194)  评论(1编辑  收藏  举报