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;
	}
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值