Codeforces Round #680 (Div. 2, based on Moscow Team Olympiad)C. Division(质因数分解+欧拉筛优化)

我来提供一个非常暴力的做法
显然,我们只需要考虑P是Q的倍数的情况。
在这种情况下
我们考虑对q的质因数分解

p 1 n 1 ∗ p 2 n 2 ∗ p 3 n 3 . . . p n n n p_1^{n_1}*p_2^{n_2}*p_3^{n_3}...p_n^{n_n} p1n1p2n2p3n3...pnnn
然后对于每一个 p 1 p_1 p1考虑
使得P不断除以 p 1 p_1 p1,直到P不能整除Q为止,记录此时的P。
以12 和6 为例
6 = 2 1 ∗ 3 1 6 = 2^1*3^1 6=2131
首先对2考虑
12 / 2 / 2 = 3   c a n ′ t   d i v   6 12/2/2=3 \ can't \ div \ 6 12/2/2=3 cant div 6
再对3考虑
12 / 3 = 4   c a n ′ t   d i v   6 12/3 =4 \ can't \ div \ 6 12/3=4 cant div 6
那么答案就是 m a x ( 3 , 4 ) = 4 max(3,4)=4 max(3,4)=4
众所周知,质因数分解的代码为

unordered_map<long long,int>ooo;
for(register ll i=2;i<=n/2;i++){
    
    
    while(n!=i){
    
    
        if(n%i==0){
    
    

            ooo[i]++;//质因数为i的数量
            n=n/i;
        }
        else
            break;
    }
         
}
if(n>1){
    
    
	ooo[n]++;
}

正常情况下,其平均复杂度为O(n^(1/4))
但在n为质数的情况下,复杂度会被卡成O(n)
然后我就被卡了
显然P<=1e9,
假如P给定50个998344353那就直接当场去世
然后我们就要考虑处理这种情况下的优化
那我们加一层素数判断?

is_prime();//判断素数
unordered_map<long long,int>ooo;
for(register ll i=2;i<=n/2;i++){
    
    
    while(n!=i){
    
    
        if(n%i==0){
    
    

            ooo[i]++;//质因数为i的数量
            n=n/i;
        }
        else
            break;
    }
         
}
if(n>1){
    
    
	ooo[n]++;
}

然而给定50个998244353*2一定会被卡。
最后,我们选用欧拉筛进行优化
,也就是先对欧拉筛的质因数先进行一个预处理。
然后,对预处理之后的数再进行判断质数+质因数分解。

这样,我们就能写成

map<long long,int>mp;
int prime[N];
int visit[N];
int cnt=0;
void Prime(){
    
    
    
    for (int i = 2;i <= N; i++) {
    
    
        if (!visit[i]) {
    
    
            prime[++cnt] = i;//纪录素数
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= N; j++) {
    
    
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
    
    
                break;
            }
        }
    }
}
void solve(ll n){
    
    
	Prime();//欧拉筛
	for(int i=1;i<=cnt;i++)//先用欧拉筛里的素数进行预处理
	{
    
    
		while(n%prime[cnt]==0)
		{
    
    
			n/=prime[cnt];
		}
	} 
	if(n==1)return ;
	if(is_prime(n)){
    
    //判断素数
        mp[n]++;
        return ;
    }	
    for(register ll i=prime[cnt];i<=n/2;i++){
    
    
        while(n!=i){
    
    
            if(n%i==0){
    
    
 
                mp[i]++;
                n=n/i;
            }
            else
                break;
        }
             
    }
    if(n>1){
    
    
    	mp[n]++;
	}
}
bool is_prime(int x){
    
    
    if(x==1)
        return false;
    if(x==2||x==3)
        return true;
    if(x%6!=1&&x%6!=5)
        return false;
    int s=sqrt(x);
    for(int i=5;i<=s;i+=6)
        if(x%i==0||x%(i+2)==0)
            return false;
    return true;
}

设原本要进行质因数分解的数为N
设预处理的最大质数为M(可近似看成质数数量*10~15)
那么时间复杂度最坏情况下为
O ( N M + N M 1 / 2 ) , 常 数 很 小 O(\frac N M+{\frac N M}^{1/2}),常数很小 O(MN+MN1/2)
或者 O ( N 1 / 2 ) O(N^{1/2}) O(N1/2)
∑ N < = 1 0 16 \sum N<= 10^{16} N<=1016的情况下均可保证在1s内。
原题AC代码,没必要用欧拉筛,暴力筛即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 10000
unordered_map<long long,int>ooo;
inline bool is_prime(int x){
    
    
    if(x==1)
        return false;
    if(x==2||x==3)
        return true;
    if(x%6!=1&&x%6!=5)
        return false;
    int s=sqrt(x);
    for(register int i=5;i<=s;i+=6)
        if(x%i==0||x%(i+2)==0)
            return false;
    return true;
}
inline void ok(ll n){
    
    
	/*可以写成欧拉筛,这里没有必要,因为N太小*/ 
	while(n%2==0){
    
    
		n/=2;
		ooo[2]++;
	}
	while(n%3==0){
    
    
		n/=3;
		ooo[3]++;
	}
	while(n%5==0){
    
    
		n/=5;
		ooo[5]++;
	}
	while(n%7==0){
    
    
		n/=7;
		ooo[7]++;
	}
	while(n%11==0){
    
    
		n/=11;
		ooo[11]++;
	}

	if(n==1)return ;
	if(is_prime(n)){
    
    
        ooo[n]++;
        return ;
    }	
    for(register ll i=11;i<=n/2;i++){
    
    
        while(n!=i){
    
    
            if(n%i==0){
    
    
 
                ooo[i]++;
                n=n/i;
            }
            else
                break;
        }
             
    }
    if(n>1){
    
    
    	ooo[n]++;
	}
}
int main()
{
    
    
	int t;
	scanf("%d",&t);
	while(t--)
	{
    
    
		ooo.clear();
		ll p,q;
		scanf("%lld%lld",&p,&q);
		if(p<q||p%q!=0||p==1){
    
    
			cout<<p<<endl;
			continue;
		}

		ll ans = 0;
 		ok(q);
 		
		for(auto iter = ooo.begin(); iter != ooo.end(); iter++)  
		{
    
    

			ll tp=p,tq=q;

			while(tp%tq==0){
    
    
				tp/=(iter->first);
			}
			ans = max(ans,tp);
		}
		cout<<ans<<endl;
	}
} 

∑ N < = 1 0 16 \sum N<= 10^{16} N<=1016考虑下面的代码(可AC)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1000000
unordered_map<long long,int>ooo;
int prime[N];
int visit[N];
int cnt=0;
inline void Prime(){
    
    
    
    for (int i = 2;i <= N; i++) {
    
    
        if (!visit[i]) {
    
    
            prime[++cnt] = i;      //纪录素数, 这个prime[0] 相当于 cnt,用来计数
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= N; j++) {
    
    
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
    
    
                break;
            }
        }
    }
}
inline bool is_prime(int x){
    
    
    if(x==1)
        return false;
    if(x==2||x==3)
        return true;
    if(x%6!=1&&x%6!=5)
        return false;
    int s=sqrt(x);
    for(register int i=5;i<=s;i+=6)
        if(x%i==0||x%(i+2)==0)
            return false;
    return true;
}
inline void ok(ll n){
    
    
	/*可用欧拉筛优化,这里没有必要*/ 
	Prime();//欧拉筛
	for(int i=1;i<=cnt;i++)
	{
    
    
		while(n%prime[cnt]==0)
		{
    
    
			n/=prime[cnt];
		}
	} 
	if(n==1)return ;
	if(is_prime(n)){
    
    
        ooo[n]++;
        return ;
    }	
    for(register ll i=2;i<=n/2;i++){
    
    
        while(n!=i){
    
    
            if(n%i==0){
    
    
 
                ooo[i]++;
                n=n/i;
            }
            else
                break;
        }
             
    }
    if(n>1){
    
    
    	ooo[n]++;
	}
}
int main()
{
    
    
	int t;
	scanf("%d",&t);
	while(t--)
	{
    
    
		ooo.clear();
		ll p,q;
		scanf("%lld%lld",&p,&q);
		if(p<q||p%q!=0||p==1){
    
    
			cout<<p<<endl;
			continue;
		}

		ll ans = 0;
 		ok(q);
 		
		for(auto iter = ooo.begin(); iter != ooo.end(); iter++)  
		{
    
    

			ll tp=p,tq=q;

			while(tp%tq==0){
    
    
				tp/=(iter->first);
			}
			ans = max(ans,tp);
		}
		cout<<ans<<endl;
	}
} 

猜你喜欢

转载自blog.csdn.net/naiue/article/details/109441227