分解质因数(邬澄瑶的公约数,Problem J. Prime Game)

质数基本知识

试除法判定质数 O ( n ) O(\sqrt{n}) O(n )

bool is_prime(int x){
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
            return false;
    return true;
}

试除法分解质因数 O ( n ) O(\sqrt{n}) O(n )

void divide(int x){
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0){
            int s = 0;
            while (x % i == 0) x /= i, s ++ ;
            cout << i << ' ' << s << endl;
        }
    if (x > 1) cout << x << ' ' << 1 << endl;
}

试除法求所有约数 O ( n ) O(\sqrt{n}) O(n )

vector<int> get_divisors(int x){
    vector<int> res;
    for (int i = 1; i <= x / i; i ++ )
        if (x % i == 0){
            res.push_back(i);
            if (i != x / i) res.push_back(x / i);
        }
    sort(res.begin(), res.end());
    return res;
}

埃氏筛 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

int primes[N], cnt; // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n){
    for (int i = 2; i <= n; i ++ ){
        if (st[i]) continue;
        primes[cnt ++ ] = i;
        for (int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}

欧拉筛 O ( n ) O(n) O(n)

int primes[N], cnt; // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n){
    for (int i = 2; i <= n; i ++ ){
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ ){
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

约数个数,约数之和

如果 N = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p k c k N = p_1^{c_1} * p_2^{c_2} * ... *p_k^{c_k} N=p1c1p2c2...pkck
约数个数: ( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c k + 1 ) (c_1 + 1) * (c_2 + 1) * ... * (c_k + 1) (c1+1)(c2+1)...(ck+1)
约数之和: ( p 1 0 + p 1 1 + . . . + p 1 c 1 ) ∗ . . . ∗ ( p k 0 + p k 1 + . . . + p k c k ) (p_1^0 + p_1^1 + ... + p_1^{c_1}) * ... * (p_k^0 + p_k^1 + ... + p_k^{c_k}) (p10+p11+...+p1c1)...(pk0+pk1+...+pkck)

邬澄瑶的公约数

传送门

在这里插入图片描述

思路:

打一个素数表,对每个数分解质因数(第一个数分解完的结果直接放到目标数组里面就好),从第二个数开始,将结果存到临时数组里面,每分解完一个数,都用临时数组去更新(不断缩小质因子种类及个数)目标数组,最后计算(快速幂)目标数组就行

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <sstream>
#define ll long long
#define debug(x) cout<<#x<<":"<<x<<endl;
#define mem(f, x) memset(f,x,sizeof(f))
#define fo(i,a,n) for(int i=a;i<n;++i)
#define fo2(i,a,n) for(int i=a;i<=n;++i)
const int INF = 0x3f3f3f3f;
using namespace std;

template<class T>inline void read(T &x){
    x=0; char c=getchar(); bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}

ll powmod(ll a, ll b, ll mod){
    ll ans=1;
    while(b){
        if(b % 2)
            ans= ans * a % mod;
        a= a * a % mod;
        b/=2;
    }
    return ans;
}

const int maxn = 10010;
int prime[maxn],pNum = 0;
int p[maxn];

void find_prime(){//埃氏筛
    memset(p,0,sizeof(p));//默认全是质数
    for(int i=2;i<maxn;i++){
        if(!p[i]){//i是质数
            prime[pNum++]=i;
            for(int j=2*i;j<maxn;j+=i)p[j]=1;
        }
    }
}

const int N=1e4+2;
int f[N],q[N];
int a[N],b[N];

void solve(){
    int n;
    cin>>n;
    fo(i,0,n)read(a[i]);
    fo(i,0,n)read(b[i]);
    fo(j,0,n){
        memset(q,0,sizeof q);//忘写了,在此WA了一发
        int t=a[j];
        int pri,cnt;
        //int sqr = (int)sqrt(1.0*t);
        //因为数据范围较小,质因数分解没有用到sqr,也不必寻找是否有唯一大于sqr的质因子了
        for(int i = 0; i < pNum ; i++){
            if(t % prime[i] == 0){
                pri = prime[i];cnt = 0;
                while(t % prime[i] == 0){cnt++;t /= prime[i];}
                if(j==0)f[pri]=cnt*b[j];
                else q[pri]=cnt*b[j];
            }
            if(t == 1) break;//剪枝,质因子已经找完了
        }

        if(j)
            fo2(i,2,N)
                if(!p[i]&&f[i])
                    f[i]=min(f[i],q[i]);
    }
    ll ans=1,mod= 1e9 + 7;
    fo2(i,2,N)
        if(!p[i]&&f[i])
            ans=ans*powmod(i,f[i],mod)%mod;
    cout<<ans%mod<<endl;
}
int main(){
    find_prime();
    solve();
    return 0;
}

在这里插入图片描述

优化:

把筛法和分解质因数放一起,每次都是把质数除尽,因为 i i i是合数时, c n t cnt cnt一定为0,所以在求质因数的过程中其实已经筛掉了合数

代码:

#include<stdio.h>
#define N 1000000007
#define ll long long
int main(){
    int n, a[10005], b[10005]; ll ans = 1;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
    for (int i = 2; i <= 10000; i++){
        int cnt = 0x3f3f3f3f;
        for (int j = 1; j <= n; j++){
            int x = 0;
            while (!(a[j] % i))x++, a[j] /= i;
            x *= b[j];
            cnt = cnt < x ? cnt : x;
        }
        for (int j = 1; j <= cnt; j++)
            ans = (ans * i) % N;
    }
    printf("%lld", ans);
    return 0;
}

在这里插入图片描述

最快的代码(4ms):

思路:

和上一份代码思路类似,通过欧拉筛来有目的的筛素数,快速幂和快读来节约时间,p[j] * p[j] <= a[i]降低内层循环,但此时需要num[a[i]]++来看这个质数在所有数的因式分解中是否都出现过,这是和上面2个代码的较大区别

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define f(i,a,b) for( int i=a;i<=b;++i)
#define ff(i,a,b) for( int i=a;i>=b;--i)
#define debug(x) cerr << #x << " : " << x << " " << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<string, string> pss;
const ll mod = 1e9 + 7;
const ll mod2 = 998244353;
const int inf = 0x3f3f3f3f;
const double tiaohe = 0.57721566490153286060651209;
ll oula(ll x) { ll res = x;f(i, 2, x / i) { if (x % i == 0) { res = res / i * (i - 1);while (x % i == 0) x /= i; } }if (x > 1) res = res / x * (x - 1);return res; }
ll quickmod(ll a, ll n, ll m) { ll s = 1;while (n) { if (n & 1) { s = s * a % m; }a = (a*a) % m;n = n / 2; }return s; }
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
void ex_gcd(ll a, ll b, ll &x, ll &y, ll &d) { if (!b) { d = a, x = 1, y = 0; } else { ex_gcd(b, a % b, y, x, d);y -= x * (a / b); } }
ll inv(ll t, ll p) { ll d, x, y;ex_gcd(t, p, x, y, d);return d == 1 ? (x % p + p) % p : -1; }
bool isPrime(ll x) { if (x == 2)return true;if (x % 2 == 0)return false;for (ll i = 2;i*i <= x;i++) if (x % i == 0)return false; return true; }
inline ll in() { char ch = getchar();ll x = 0, f = 1;while (ch<'0' || ch>'9') { if (ch == '-')f = -1;ch = getchar(); }while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0';ch = getchar(); }return x * f; }
//double a = log(n) +tiaohe + 1.0 / (2 * n);
double eqa = (1 + sqrt(5.0)) / 2.0;
double E = 2.7182818284;
const double eps = 1e-8;
const int N = 1e4 + 1000;
int a[N], b[N];
int p[N + 1], cnt;
bool vis[N + 1];
int mx[N],num[N];
void get(){
	//p存质数(从1开始),vis存是否是质数(0是质数)
	f(i, 2, N) {
		if (!vis[i])p[++cnt] = i;
		for (int j = 1;p[j] <= N / i;j++) {
			vis[p[j] * i] = true;
			if (i%p[j] == 0)break;
		}
	}
	f(i, 1, N - 1)mx[i] = 1e9;
}
int main(){
	get();
	int n = in();
	f(i, 1, n)a[i] = in();
	f(i, 1, n)b[i] = in();
	f(i, 1, n){
		for (int j = 1;p[j] * p[j] <= a[i];j++) {
			if (a[i] % p[j] == 0) {
				int cnt = 0;
				while (a[i] % p[j] == 0) {
					a[i] /= p[j];
					cnt++;
				}
				mx[p[j]] = min(mx[p[j]], cnt*b[i]);
				num[p[j]]++;
			}
		}
		if (a[i] > 1)mx[a[i]] = min(mx[a[i]], b[i]), num[a[i]]++;
	}
	ll res = 1;
	f(i, 2, N-1) 
		if (!vis[i]&&num[i]==n) 
			res = res * quickmod(i, mx[i], mod) % mod;						
	cout << res << endl; 
	return 0;
}

在这里插入图片描述

Problem J. Prime Game

在这里插入图片描述

思路:

去分析每个出现的质数对答案的贡献

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#include <set> 
#include <map>
#include <sstream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define f(i,a,n) for(int i=a;i<n;++i)
#define ff(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
typedef double dbl;
typedef pair<int, int> pi;
const int INF = 0x3f3f3f3f;
inline ll in() { char ch = getchar();ll x = 0, f = 1;while (ch<'0' || ch>'9') { if (ch == '-')f = -1;ch = getchar(); }while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0';ch = getchar(); }return x * f; }
const int N=1e6+5;
int a[N];
int p[N + 1], cnt;
bool vis[N + 1];
void get(){
	//p存质数(从1开始),vis存是否是质数(0是质数)
	f(i, 2, N) {
		if (!vis[i])p[++cnt] = i;
		for (int j = 1;p[j] <= N / i;j++) {
			vis[p[j] * i] = true;
			if (i%p[j] == 0)break;
		}
	}
}
vector<ll> v[N];
int main(){
	get();
    int n=in();
    ff(j,1,n){
    	a[j]=in();
    	//每个数因式分解,把其编号放到对应素数的数组里
    	for(int i=1;p[i]*p[i]<=a[j];i++){
	        if(a[j]%p[i]==0){
	            v[p[i]].push_back(j);
	            while(a[j]%p[i]==0)a[j]/=p[i];
	        }
	    }
	    if(a[j]>1)v[a[j]].push_back(j); 
	}
    ll ans=0;
    f(i,1,cnt){
    	if(!v[p[i]].size())continue;
    	ans+=v[p[i]][0]*(n-v[p[i]][0]+1);
        for(int j=1; j<v[p[i]].size(); j++)
            ans+=(v[p[i]][j]-v[p[i]][j-1])*(n-v[p[i]][j]+1);   	
	} 
	cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值