8月月赛A 洛谷p3861

题目描述

给定一个整数 nnn,求将 nnn 分解为互不相同的不小于 222 的数的乘积的方案数。答案模 998244353998244353998244353

输入输出格式

输入格式:

第一行一个整数 TTT,表示数据组数。

接下来 TTT 行,每行一个整数 nnn,意义如描述所述。

输出格式:

一共 TTT 行,每行一个整数,表示答案。

输入输出样例

输入样例#1:
1
688
输出样例#1:
6

说明

样例中,因为

688=2×4×86=2×8×43=2×344=4×172=8×86=16×43688 = 2 \times 4 \times 86= 2 \times 8 \times 43= 2 \times 344= 4 \times 172= 8 \times 86= 16 \times 43688=2×4×86=2×8×43=2×344=4×172=8×86=16×43

所以答案为 666

对于 10%10\%10% 的数据,保证 nnn 为质数

对于 20%20\%20% 的数据,保证 2≤n≤1042 \leq n \leq 10^42n104

对于 50%50\%50% 的数据,保证 2≤n≤107 2 \leq n \leq 10^72n107

对于 100%100\%100% 的数据, 保证 2≤n≤1012 2 \leq n \leq 10^{12}2n1012

所有数据满足 1≤T≤51 \leq T \leq 51T5


对于每一个N,我们先求出它的第iii小因子facifac_{i}faci, 然后记录下因子jjj所对应的位置posjpos_{j}posj。这里有一个小技巧:

+ififif j≤Nj \le \sqrt{N}jN, 则令pos1j=posjpos1_{j}=pos_{j}pos1j=posj

+ififif j>Nj > \sqrt{N}j>N, 则令pos2Nj=posjpos2_{\frac{N}{j}}=pos_{j}pos2jN=posj

这样就可以把空间压缩为O(N)O(\sqrt{N})O(N)

然后令dpijdp_{ij}dpij为把facifac_{i}faci分解成若干个小于等于facjfac_{j}facj的数(包括111)的积的方案总数,则:

dpij=dpij−1dp_{ij}=dp_{i j-1}dpij=dpij1

ififif i=ji=ji=j, dpij++dp_{ij}++dpij++

ififif faci≡0,mod(facj)fac_{i} \equiv 0,mod( fac_{j})faci0,mod(facj), dpij+=dpposfacifacjj−1dp_{ij}+=dp_{pos_{\frac{fac{i}}{fac_{j}}} j-1}dpij+=dpposfacjfacij1

#include<iostream>
#include<cstring>
#include<map>
#include<cmath>
#include<algorithm>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const long long MOD=998244353,MAXN=11000;
long long n,fac[MAXN],q;
int f[MAXN][MAXN],pos1[1000011],pos2[1000010],num;
map<long long,int> ans;
int main()
{
	ios::sync_with_stdio(false);
	int T,i,j;
	cin>>T;
	while(T--){
		num=0;
		cin>>n;
		if(ans[n]){
			cout<<ans[n]<<endl;
			continue;
		}
		q=sqrt(n);
		if(n%q==0){
			fac[++num]=q;
			if(q*q!=n){
				fac[++num]=n/q;
			}
		}
		f(i,1,q-1){
			if(n%i==0){
				fac[++num]=i;
				fac[++num]=n/i;
			}
		}
		f(i,1,num){
			f(j,1,num){
				f[i][j]=0;
			}
		}
		sort(fac+1,fac+1+num);
		f(i,1,num){
			f[i][i]=1;
			if(fac[i]<=q){
				pos1[fac[i]]=i;
			}
			else{
				pos2[n/fac[i]]=i;
			}
		}
		f(i,1,num){
			f(j,1,num){
				f[i][j]+=f[i][j-1];
				if(i<=j||fac[i]%fac[j]){
				//	cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
					continue;
				}
				long long tmp=fac[i]/fac[j];
				if(tmp<=q){
					f[i][j]+=f[pos1[tmp]][j-1];
				}
				else f[i][j]+=f[pos2[n/tmp]][j-1];
			//	cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
				f[i][j]%=MOD;
			}
		}
		ans[n]=f[num][num]-1;
		cout<<f[num][num]-1<<endl;
	}
	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值