题解:P3861 拆分
dayz_break404 · · 题解
很妙的一道计数 dp 题。
根据数学直觉,我们可以感受到就算是一个很大的数,它的因数个数也是较小的。由于
由于题目让我们计算方案数,可以用 dp 计算方案数。
考虑先将
记
当前的第
但是,这里会出现一个问题,由于本题值域很大,
由于要排除
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+20;
const int maxm=2e6+20;
const int mod=998244353;
#define ll long long
ll t,idx;
ll a[maxn],dp[maxn][maxn],x,pos1[maxm],pos2[maxm];
ll n;
inline ll get(ll i,ll j){
x=a[i]/a[j];
ll k=(ll)(sqrt(n));
if(x<=k) return pos1[x];
else return pos2[n/x];
}
inline void solve(){
idx=0;
for(ll i=1;i<=n/i;i++){
if(n%i==0) {
a[++idx]=i;
if(i!=n/i) a[++idx]=n/i;
}
}
sort(a+1,a+idx+1);
for(ll i=1;i<=(idx+1)/2;i++) pos1[a[i]]=i,pos2[a[i]]=idx-i+1;//记录下标
for(ll i=1;i<=idx;i++) dp[i][1]=(i==1);//初始状态
for(ll i=1;i<=idx;i++){
for(ll j=2;j<=idx;j++){
dp[i][j]=dp[i][j-1];
if(a[i]%a[j]==0) dp[i][j]=(dp[i][j]+dp[get(i,j)][j-1])%mod;
}
}
printf("%lld\n",dp[idx][idx]-1);
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
solve();
}
return 0;
}