我的蒟蒻暴力法:
很暴力的算法,但是过了。主要就是想着将所有的素数都先dia出来,然后将n对每一个素数都进行一次相爱相杀,同时记录用上的素数和指数。顺便学了个素数筛,之所以把全部素数都提前弄出来,其实就是为了防止多次重复计算素数。其实看到范围限制的时候就可以放弃这个策略了,因为全部的测试数据满足:1<n<=1e10 且 1<k,q≤10,相当于最多只重复个10次而已。
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const long long N=1e8;
#define ll long long
ll n;
int k,q;
vector<bool>statue(N,true); //存放,是否为素数,默认全为素数
vector<int>primes;
void getVec(vector<pair<int,int>>&vec,ll n)
{
for(int i=0;i<primes.size();i++)
{
int a=primes[i];
int b=0;
if(n%a==0)
{
while(n%a==0) //如果是倍数
{
n/=a;
b++;
}
vec.push_back({a,b}); //将素数和指数加进去
}
}
}
//欧拉筛,还是现学的
void euler_sieve() //这里使用欧拉筛将所有的素数罗列
{
statue[0]=statue[1]=false; //默认0,1都不是素数
for(ll i=2;i<=N;i++) //从2到N遍历一遍
{
if(statue[i]) //如果当前是素数
primes.push_back(i); //存起来
for(ll j=0;j<primes.size();j++) //不论是不是素数,都要将该数i和素数集都乘一次
{
if(i*primes[j]>N) break; //如果超出标记范围,就跳出
statue[i*primes[j]]=false; //标记合数
if(i%primes[j]==0) break; //如果标记到最小素倍数(i是primes[j])那就溜啦
//这一步是为了不重复标记
}
}
}
int main()
{
cin>>q;
euler_sieve();
while(q--)
{
cin>>n>>k;
ll div=1;
vector<pair<int,int>>vec;
getVec(vec,n);
for(int i=0;i<vec.size();i++)
{
if(vec[i].second<k) //逆向,除去
div*=pow(vec[i].first,vec[i].second);
}
cout<<n/div<<endl;
}
return 0;
}
下面是另一篇题解[CCF-CSP——因子化简)
他没有用欧拉筛选出所有的素数,而是在遍历过程中判断是否为素数,相当于而言减少很多不必要的计算。但是借此机会学习我上面提到的欧拉筛也是一桩美事~
#include<bits/stdc++.h>
using namespace std;
//判断素数
int isPrime(int x) {
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
return 0;
}
}
return 1;
}
void query(long long n, int k) {
long long temp = n;
long long final = 1;
for(int i=2; i * i <= n; i++) {
if(isPrime(i)){
int num = 0;
while(n%i==0){
n/=i;
num++;
}
// printf("%d\n", num);
if(num>=k){
while(num--){
final*=i;
}
}
}
}
if(final==temp){
printf("%lld\n", temp);
}else if(final==1){
printf("1\n");
}else{
printf("%lld\n", final);
}
}
int main() {
int q;
scanf("%d", &q);
for(int i=0; i<q; i++) {
long long n;
int k;
scanf("%lld%d", &n, &k);
query(n, k);
}
return 0;
}