一.前置知識
莫比烏斯反演
數論分塊
二.題意及分析
①題意:給出1~n,求其中子序列能組成等比數列的數目
②初步分析:設公比爲,首項爲p,末項爲q,都爲整數。
由於很容易知道q必須要整除a^(k-1)。而這樣末項q有種。
而對於分母上,每一個a,由開頭公比定義,還有一個約束條件,就是必須有一個小於a並與a互質的數b,才能與他配對
因此兩條件需要同時滿足,得出式子:
③數學分析:
a.首先是很經典的杜教篩,這裏分塊一次鏈接
b.然後考慮後半部分:
k=1自成等比數列,k=2任意取出兩數也必成等比數列,結果易得。
k=3,分母是平方,所以是常規的分塊(第二次分塊)
k>3,暴力算*phi(a),然後累加求和即可(見代碼註釋)因爲n最大1e17,需要枚舉個,最多也是不到1e6
三、代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 40000000 + 10;//小範圍會莫名T ,大範圍會MLE
const int mod = 998244353;
int pri[maxn], vis[maxn], cnt = 0, phi[maxn], sum[maxn];
void init() {//素數篩
vis[1] = phi[1] = 1;
cnt = 0;
for (int i = 2; i < maxn; i++) {
if (!vis[i]) {
pri[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && i * pri[j] < maxn; j++) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
for (int i = 1; i < maxn; i++) {
sum[i] = (sum[i - 1] + phi[i]) % mod;
}
}
//杜教篩部分
unordered_map<ll, int> mp;
ll get_s1(ll x) {
x %= mod;
ll tmp = x * (x + 1) / 2;
return tmp % mod;
}
ll S_phi(ll x) {//左半部分歐拉函數前綴和
if (x < maxn) return sum[x];
if (mp.count(x)) return mp[x];
ll ans = 0;
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ans = (ans + (r - l + 1) * S_phi(x / l) % mod) % mod;
}
ans = (get_s1(x) - ans + mod) % mod;
return ans;
}
//歐拉函數前綴和,杜教篩結束
ll get_sqrt(ll a) {
ll x = (ll) sqrt(a);
return x;
}
ll solve(ll n) {
ll ans = (n % mod) * (n % mod + 1) / 2;
//k=1爲n,k=2爲直接求和
ans %= mod;
//k=3即n/a^2時 ,常規分塊
for (ll l = 2, r; l * l <= n; l = r + 1) {
r = get_sqrt(n / (n / l / l));
ans = (ans + n / l / l % mod * (S_phi(r) - S_phi(l - 1) + mod) % mod) % mod;
}
//k>3,n/a^(k-1 ),暴力求和begin
for (ll a = 2;; a++) {
ll cur = a * a;
if (cur > n / a) break;
cur *= a;
while (cur <= n) {
//*phi
ans = (ans + n / cur % mod * phi[a]) % mod;
if (cur > n / a) break;
cur *= a;
}
}
//暴力end
return ans;
}
int main() {
int T;
init();
scanf("%d", &T);
while (T--) {
ll n;
scanf("%lld", &n);
printf("%lld\n", solve(n));
}
return 0;
}