1. 程式人生 > >特殊整除分塊的優化

特殊整除分塊的優化

整除分塊十分naive,但是卡常之後就不清真了。

如果需要計算\[\sum_{i=1}^n\lfloor \frac{n}{i} \rfloor\]
有一個naive的做法就是

for (long long i=1,la; i<=n; i=la+1){
    la=n/(n/i);
    ans+=(n/i)*(la-i+1);
}

但是,這樣不僅根號有2的常數,瓶頸上還有3次除法(可優化至2次),如果n是一個較大的數,跑起來很man。
今天突然看到了鬆1自己的提交,於是興沖沖地又複習了一下優越的演算法。
首先推式子
要求\[\sum_{i=1}^n \sum_{j=1}^n [i*j \leq n] \]


可拆為\[ \sum_{i=1}^{ \lfloor \sqrt {n} \rfloor} \sum_{j=1}^n [i*j \leq n] +\sum_{i= \lfloor \sqrt{n} \rfloor +1}^n \sum_{j=1}^n [i*j \leq n] \]
變換邊界條件\[ \sum_{i=1}^{ \lfloor \sqrt {n} \rfloor} \sum_{j=1}^n [i*j \leq n] +\sum_{i= \lfloor \sqrt{n} \rfloor +1}^n \sum_{j=1}^{\lfloor \sqrt{n} \rfloor} [i*j \leq n] \]

在把前後兩項變得一樣
\[ \sum_{i=1}^{ \lfloor \sqrt {n} \rfloor} \sum_{j=1}^n [i*j \leq n] +\sum_{i=1}^n \sum_{j=1}^{\lfloor \sqrt{n} \rfloor} [i*j \leq n]-\sum_{i=1}^{ \lfloor \sqrt {n} \rfloor} \sum_{j=1}^{\lfloor \sqrt {n} \rfloor}[i*j \leq n]\]
合併一下
\[2* \sum_{i=1}^{\lfloor \sqrt {n} \rfloor} \sum_{j=1}^n [i*j \leq n] - ( \lfloor \sqrt{n} \rfloor)^2 \]

換一種表示
\[2* \sum_{i=1}^{\lfloor \sqrt{n} \rfloor} \lfloor \frac{n}{i} \rfloor -(\lfloor \sqrt{n} \rfloor) ^2 \]
就可以快速計算啦!

%:pragma GCC target("avx")
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include <iostream>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
int main(){
    ll n; cin>>n;
    ll ans=0;
    ll p=sqrt(n);
    for (ll i=p; i; --i) ans+=n/i;
    ans=ans*2-p*p;
    cout<<ans<<endl;
}