目录
质数基本知识
试除法判定质数 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=p1c1∗p2c2∗...∗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;
}