https://www.oj.swust.edu.cn/problem/show/2810
題意:很簡單就是求題目上面那個式子的和。
做法:直接開始推公式。
然後,將前後兩部分分開
然後對於前面的一部分直接可以由公式計算得到:
我們現在考慮後面一部分怎麼計算。我剛剛拿到的時候一點頭緒都沒有。
但不過我們做題做多了,經常可以發現這種式子一般可以化成,兩個上界爲n的和式:
這樣是對的嗎,很可惜,我打了一下表發現並不對。。。。。
發現是漏掉一些情況,其實我們可以這樣想一個對稱矩陣,你現在想用一個矩陣的所有元素的值,加起來求下三角的值
這是你會發現對角線的值會有問題,這時你會把對角線的值乘上2,在平方。自己yy一下吧。
正確公式這樣的
其實我是打表結合瞎猜的
關於前面一部分可以直接,求出來。
我們重點討論後面一部分怎麼做,打了一下表發現根本沒有規律可循,好像也不可以莫比烏斯和歐拉。。。
還是首先先進行變形看看吧(其實是亂搞):
我覺得這一步有一點點不好想到,弄了好久,這樣i和j就可以分開。
我們令:
然後發現原式可以這樣變化:
這樣以及很不錯了以及可以分塊求這一部分了,但是,如果直接這樣前面的i雖然可以用公式,而後面的則是
這個複雜度,雖然到後面會慢慢的降低,但不過前面的複雜的會很大,已經T了。
然後我們觀察,或者,好像不能篩吧。。。看看有沒有什麼性質,比如說什麼積性函數啊,用杜教篩等等。很遺憾打了一天表毛關係都沒有找到。
我們還是來進行一些稍微嚴格(亂搞)的推導吧:
我們令:,我們發現g(n)的前綴和就是h(n)
這個式子除了i等於n的約數情況下其他全部爲零。
並且等於約數情況下,就等於i 所以就是個約數和:
不信?你可以打打表。
這肯定是一個積性函數,然後我們就可以線性篩了。預處理,他的前面的一部分的和。這樣時間複雜度就可以得到優化了。
其實,我真正的經歷是這樣的:確實弄了好久發現h(n)的差是一個積性函數,但不過並不知道是約數和,然後就在哪裏線性篩。
篩了大半天,才篩出來和打表一樣,然後想着進行狄利克雷卷積,用杜教篩,然後捲了大半天,終於才發現就是一個約數和。
然後就百度一下,沒有找到。然後我尋思這一想,直接對剩下的h(n)進行分塊暴力搞,這樣複雜度也不高吧,好像和杜教篩差不多耶。
我記住了約數和的前綴和可以這樣求了。。。
我就接着上面那樣隨便搞了一下就過了。。。。
我們來分析一下複雜度(瞎說):
其實後面一部分的p是預處理的大小,q也是和p有關係是這樣的,我們近似處理一下:
,我們根據均值不等式,得出了是n的0.75次方差不多最合適,但不過由於內存,加上答案會爆long long 我們用int128來處理,會爆內存,因此我們開2000000萬差不多了。
題解貌似是n^3/2...
#include "bits/stdc++.h"
using namespace std;
///typedef long long ll;
typedef __int128 ll;
const int N = 20000000 + 10;
int vis[N], cnt = 0;
ll f[N], h[N];
long long n, pri[N], num[N];
void print(__int128 x) {
if (!x) {
puts("0");
return;
}
string ret = "";
while (x) {
ret += x % 10 + '0';
x /= 10;
}
reverse(ret.begin(), ret.end());
cout << ret << endl;
}
void init() {
f[1] = vis[1] = 1;
for (int i = 2; i < N; i++) {
if (!vis[i]) {
pri[++cnt] = i;
f[i] = i + 1;
num[i] = i;
}
for (int j = 1; j <= cnt && i * pri[j] < N; j++) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) {
if (i / num[i] != 1) f[i * pri[j]] = f[num[i] * pri[j]] * f[i / num[i]];
else f[i * pri[j]] = 1LL * i * pri[j] + f[i];
num[i * pri[j]] = num[i] * pri[j];
break;
}
f[i * pri[j]] = f[i] * f[pri[j]];
num[i * pri[j]] = pri[j];
}
}
for (int i = 1; i < N; i++) {
h[i] = h[i - 1] + f[i];
}
}
ll get_last(ll n) {
ll ret = 0;
for (ll i = 1; i * i <= n; i++) {
ret += (n / (i * i)) * i * i;
}
return ret;
}
ll get_s1(ll x) {
ll ret = x * (x + 1) / 2;
return ret;
}
ll fun(ll x) {
if (x < N) return h[x];
ll ret = 0;
for (ll l = 1, r; l <= x; l = r + 1) {
r = x / (x / l);
ret += (x / l) * (get_s1(r) - get_s1(l - 1));
}
return ret;
}
ll solve(ll n) {
ll ans = n * n * (n + 1) / 2;
ll pre = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
pre += (get_s1(r) - get_s1(l - 1)) * fun(n / l);
}
ans = ans - (pre + get_last(n)) / 2;
return ans;
}
int main() {
int T;
init();
scanf("%d", &T);
while (T--) {
scanf("%lld", &n);
print(solve(n));
}
return 0;
}