ABC 169D Div Game 题解 - 质因数分解+贪心

题目链接

题目大意

给出一个正整数 n n n,并对它进行若干次操作
对于每次操作,选择一个正整数 x x x,满足 x = p e x=p^e x=pe x x x 跟其他操作中用过的 x x x 不一样(每个 x x x 只能用一次)。其中 p p p 为质数, e e e 为正整数
n n n 变成 n x \frac{n}{x} xn
问最多可以进行多少次这样的操作

首先,我们对 n n n 进行质因数分解,复杂度 O ⁡ ( n ) \operatorname{O}(\sqrt{n}) O(n )
n n n 就可以表示为 p 1 e 1 × p 2 e 2 × ⋯ × p m e m p_{1}^{e_1} \times p_{2}^{e_2} \times \dots \times p_{m}^{e_m} p1e1×p2e2××pmem

对于所有 ( 1 ≤ i ≤ m ) (1 \le i \le m) (1im),我们要把 p i e i p_i^{e_i} piei 拆成 a 1 × a 2 × ⋯ × a k a_1 \times a_2 \times \dots \times a_k a1×a2××ak,满足所有 a a a 都是 p i p_i pi 的正整数次幂,使得不相同的 a i a_i ai 的个数最大

由于所有 a i a_i ai 都是同底的,所以,上面的问题可以转化:
对于所有 ( 1 ≤ i ≤ m ) (1 \le i \le m) (1im),将 e i e_i ei 拆成若干个正整数之和,使得不同的数的个数最大,用贪心/背包实现即可

#include
#include
#include
using namespace std;
const long long Maxn=60,Maxm=1000000+10;
long long f[Maxn],n,m,ans;
bool vis[Maxm];
inline long long read()
{
	long long s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
long long dp(long long cnt) // 这里写的是背包
{
	memset(f,0,sizeof(f)); // f[i] 表示 i 最多可以拆成出多少个不同的正整数
	for(long long i=1;i<=cnt;++i)
	for(long long j=cnt;j>=i;--j)
	f[j]=max(f[j],f[j-i]+1);
	return f[cnt];
}
bool check(long long x)
{
	if(x==1)return 0; // O(sqrt(n)) 判断质数
	for(long long i=2;i*i<=x;++i)
	if(x%i==0)return 0;
	return 1;
}
int main()
{
//	freopen("in.txt","r",stdin);
	n=read();
	for(long long i=2;i*i<=n;++i)
	{
		if(vis[i])continue;
		for(long long j=(i<<1);j*j<=n;j+=i)
		vis[j]=1;
	} // 筛素数
	vis[1]=1,dp(55),m=n;
	while(n!=1) // 一边质因数分解一边计算
	{
		for(long long i=2;i*i<=n;++i)
		{
			if(n%i || vis[i])continue;
			long long cnt=0;
			while(m%i==0 && m)++cnt,m/=i;
			long long tmp=f[cnt];
			ans+=tmp;
		}
		if(n==m)break;
		n=m;
	}
	if(check(n))++ans;
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(题解,AtCoder)