2019HDU多校九6683--Rikka with Geometric Sequence(杜教筛+分块)

参考链接

一.前置知识

莫比乌斯反演

数论分块

杜教筛(这里是筛欧拉函数,参见此文二、①--b)

 

二.题意及分析

①题意:给出1~n,求其中子序列能组成等比数列的数目

②初步分析:设公比为,首项为p,末项为q,都为整数。

由于很容易知道q必须要整除a^(k-1)。而这样末项q有种。

而对于分母上,每一个a,由开头公比定义,还有一个约束条件,就是必须有一个小于a并与a互质的数b,才能与他配对

因此两条件需要同时满足,得出式子:

③数学分析:

a.首先\sum_{i=2}^n\varphi(i)是很经典的杜教筛,这里分块一次链接

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;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值