JATC的数学老师为了不让同学们感到厌倦,总是出一些有趣的题目。今天的题目是这样的:
给定一个整数n,您可以对它进行如下操作:
您可以对这些操作进行零次至任意次。那么n可以达到的最小值是多少 ?达到最小值需要进行操作的次数又是多少?
显然,班里没有同学能够解决这个问题,您能够帮帮他吗?
一开始确实没什么头绪。。。。但是当把样例里面的n进行质因数分解后,再与答案进行对比,有了奇怪的发现。
首先把一个数分解为a^x1 * b^x2 * c^x3 * ......d^ x..其中a,b,c....等数都为质数,显然,这些质数是不能通过开方运算来消除的,因此,所能得到的最小数就是这些质数相乘的结果。
解决了最小数的问题,那么怎么得到最少的步数呢?由于是开方运算,每开一次方,所有质数的质数都除以二,我们最终的目标是把所有质数的质数都降为1,所以通过一次乘法将所有质数的指数都变为2^y是一定要做的,所得到的y,加上你做的一次乘法就是步数
具体的步骤
#include<bits/stdc++.h> using namespace std; bool vis[10000005]; vector<int> prime; struct num{ int n,t; num(){} num(int nn,int tt){ n = nn; t = tt; } };//记录分解后每个质数及其次数,n为该质数 vector<num> divv; int m; int x(){//求得最小的y int now = 1; int tim = 0; while(now < m){ now *= 2; tim++; } return tim; } int main(){ int d; cin >> d; if(d == 1){//特殊情况特殊处理 cout << 1 << " 0" ; return 0; } for(int i = 2; i <= 1000005; i++){//线性筛法,得到数据范围内的质数,便于质因数分解 if(!vis[i]){ vis[i] = 1; prime.push_back(i); } for(int j = 0; j < prime.size(); j++){ if(i * prime[j] > 1000005)break; vis[i * prime[j]]= 1; if(i % prime[j] == 0)break; } } int cnt = 0,time = 0; while(1){ if(d % prime[cnt] == 0){ d /= prime[cnt]; time++; } else{ if(time != 0)divv.push_back(num(prime[cnt],time));//质因数分解并做记录 cnt++; time = 0; } if(d == 1 && time == 0)break; } int ans = 1; bool flag = 1; for(int i = 0; i < divv.size(); i++){//检查一下是否需要进行一次乘法 if(i >= 1){ if(divv[i - 1].t != divv[i].t)flag = 0;//如果不能做到任意两个质数之间的次数相等,那么需要做一次乘法,先做一个标记 } ans *= divv[i].n; m = max(m,divv[i].t); } int g = pow(2,x()); if(g == m && flag){ cout << ans << " " << x(); } else if(g > m || !flag){//如果有质数之间的次数不相等或者所有质数的次数相等但不是2^y这样的形式,就需要做一次乘法 cout << ans << " " << x() + 1; } }