O(√N) Ver
map<ll, ll> factorize(ll n){
map<ll, ll> mp;
ll sq = sqrt(n);
FOR(i, 2, sq+1){
while(n%i==0){
mp[i]++;
n/=i;
}
}
if(n!=1){
mp[n]++;
}
return mp;
}
ll yaku(ll n){
auto mp = factorize(n);
ll ret = 1;
for(auto pa : mp){
ret *= (pa.second+1);
}
return ret;
}
SPF配列 (O(log(N))の前準備)
- SPF : smallest prime factor
- vを割れる最小の素数を事前に準備しておく
- その素数でまず割って、小さくなった値に対しても再帰的に割ることで「割れるか確かめる」ことが省ける
- これは計算量 log(N) となり、√Nよりも大分速い
素因数分解(高速版)O(log(N))
const int N_MAX = 2000000;
ll spf[N_MAX];
void prepare_factorize(){
rep(i,N_MAX) spf[i] = i;
for(int p=2; p*p <= N_MAX; p++){
for(int i=p; i<N_MAX; i+=p){
if(spf[i]==i) spf[i] = p;
}
}
}
map<ll, ll> factorize_fast(ll n){
if(spf[1]==0){
p("please initialize");
exit(0);
}
map<ll, ll> mp;
while(n!=1){
ll p = spf[n];
mp[p]++;
n/=p;
}
return mp;
}
ll yaku(ll n){
auto mp = factorize_fast(n);
ll ret = 1;
for(auto pa : mp){
ret *= (pa.second+1);
}
return ret;
}
ll f(ll v){
return v - yaku(v);
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
ll X;cin>>X;
prepare_factorize();
vector<pair<ll, PII>> V;
FOR(a, 1, X){
ll b = X-a;
if(b<=0) continue;
ll v = abs(f(a)-f(b));
V.push_back(MP(v, MP(a,b)));
}
sort(ALL(V));
ll mi = V[0].first;
for(auto triplet : V){
if(triplet.first==mi){
ll a = triplet.second.first;
ll b = triplet.second.second;
p2(a,b);
}
}
return 0;
}
高速素因数分解(logA)を約数列挙に適用する
- 「高速約数列挙」としてよく言われているものは O(sqrt(A)) のものが多い
- 上記osa_kで素因数分解した後、その結果を使えば O(log(A)) で求められそうなのでやってみた
- 問題:D - Not Divisible
- 素因数分解した結果、各素因数ごとに何乗を採用するかをdfsで全探索して約数を列挙している
- 画像でいうと下記
void dfsd(ll cur_idx, ll cur_val, VI&Y,
vector<PII>& mp
){
ll N = mp.size();
if(cur_idx==N){
Y.push_back(cur_val); return;
}
ll v = mp[cur_idx].first;
ll c = mp[cur_idx].second;
ll mul = 1;
rep(p, c+1){
dfsd(cur_idx+1, cur_val*mul, Y, mp);
mul *= v;
}
return;
}
VI calc_devisors_fast(ll a){
VI Y;
auto mp = factorize_fast(a);
vector<PII> V;
for(auto pa : mp){
V.push_back(pa);
}
dfsd(0, 1, Y, V);
return Y;
}