P3861 拆分【dp优化技巧 数学】

拆分 - 洛谷

核心思路

维护因数位置

使用了一种dp技巧,对特例改为范围。

记f(i,j) 表示a[i]被拆分成若干个(数量不限)<= a[j] 的因子的方案数。

则拓宽边界采用f[i][j] += f[pos][j-1];//本质上就是加上了a[j]*a[pos] 的方案 (单对多)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+7;
long long f[7050][7050];//f[i][j]表示将ai拆成若干个小于aj的数的方案数 
int pos[3][2*N];
int a[N];
int tot;
int mod = 998244353;
signed main(){
	int t;
	cin>>t;
	while(t--){
		memset(f,0,sizeof(f));
		memset(pos,0,sizeof(pos));
		int n;
		cin>>n;
		tot = 0;
		for(int i = 1;i*i <= n;i++){
			if(n%i == 0){
				a[++tot] = i;
				if(n/i != i)a[++tot] = n/i;
			}
		}
		sort(a+1,a+1+tot);
		for(int i = 1; (i << 1) <= tot + 1; i++) //记录pos 
		{
			pos[1][a[i]] = i;
			pos[2][a[i]] = tot - i + 1;
			//排序对称性 
		}
		for(int i = 1;i <= tot;i++){
			if(i == 1) f[i][1] = 1; //初始化
			for(int j = 2;j <= tot;j++){
				f[i][j] = f[i][j-1];//不使用aj的情况 
				if(i < j)continue;
				if(a[i]%a[j] == 0){
					int tmp =  a[i]/a[j];
					int p ;
					if(tmp<=(long long)sqrt(n))p = pos[1][tmp];
					else p = pos[2][n/tmp];
					f[i][j] = (f[i][j] + f[p][j-1]) % mod;
				}
			}
		}
		cout<<f[tot][tot]-1<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值