題目連結
題意:
給定n
,k
,求 ∑(k mod i) {1<=i<=n}
其中 n,k<=10^9
。
即 k mod 1 + k mod 2 + k mod 3 + … + k mod n
的值。
我們先來看商之和。
給定n
,k
,求∑(k/i) {1<=i<=n}
其中/
為整除。
可以得到一個引理,k/i
值的個數不超過2*√k
種。
證明:k
整除小於√k
的數,都會有一個不同的結果;k
整除大於√k
的數,結果肯定小於√k
,所以最多也只能有√k
種結果。
於是我們可以列舉結果的取值累加。是O(√k)
級別的。
程式碼可以這樣寫:
LL sum(LL n,LL k){ //calc sigma(k/i) 1<=i<=n LL sum = ; ; i <= n ; i ++ ){ LL a = k / i ; LL b = k / a ; b = min(b,n) ; sum += a * (b-i+) ; } return sum; }
其中a
是k/i
的值,b
是最大得到k/i
這個值的數,b-i+1
為取得同一個值的區間長度。
然後來看餘數之和:
我們知道 a mod b == a - a/b*b
(整除)。
於是 ∑(k mod i) {1<=i<=n}
就可以寫成n*k-∑k/i*i {1<=i<=n}
對於k/i
值相同的一段,後面那一項是一個等差數列,求和就好了。
/************************************************************** Problem: 1257 User: zrts Language: C++ Result: Accepted Time:8 ms Memory:1272 kb ****************************************************************/ #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> //by zrt //problem: using namespace std; typedef long long LL; const int inf(0x3f3f3f3f); ); LL n,k; int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif scanf("%lld%lld",&n,&k); LL ans=n*k; LL sub=; ;i<=n&&i<=k;i++){ LL a=k/i;LL b=k/a; b=min(b,n); sub+=a*(i+b)*(b-i+)/; i=b; } printf("%lld\n",ans-sub); ; }
另有一道題:切巧克力。在SegmentFault上有人提問,連結。我的回答就是用了與這個類似的方法。