[luogu p3601] 签到题

传送门

签到题

题目背景

这是一道签到题!

建议做题之前仔细阅读数据范围!

题目描述

我们定义一个函数:qiandao(x)为小于等于x的数中与x不互质的数的个数。

这题作为签到题,给出l和r,要求求\(\sum_{i=l}^r qiandao(i)~mod~666623333\)

输入输出格式

输入格式

一行两个整数,l、r。

输出格式

一行一个整数表示答案。

输入输出样例

输入样例 #1

233 2333

输出样例 #1

1056499

输入样例 #2

2333333333 2333666666

输出样例 #2

153096296

说明

对于30%的数据,\(l,r\leq 10^3\)

对于60%的数据,\(l,r\leq 10^7\)

对于100%的数据,\(1 \leq l \leq r \leq 10^{12}\)\(r-l \leq 10^6\)

分析

\(1 \leq l \leq r \leq 10^{12}\),因此朴素质数筛肯定是过不了了。

但是我们能注意到:

\(r-l \leq 10^6\)

P1835 内味了,因此不难想到是筛出 \(1 \sim r - l\) 中所有质数,然后进行处理。

然后再分析题面要我们求什么:\(\sum_{i=l}^r qiandao(i)~mod~666623333\)

再来看qiandao函数的定义:

qiandao(x)为小于等于x的数中与x不互质的数的个数

那么很显然,\(qiandao(x) = x - \varphi(x)\)

所以做法就是,枚举 \(1 \sim r - l\) 中每个质数 \(p\),计算 \(p\)\(l \sim r\) 间某个数 \(i\)\(\varphi(i)\) 的贡献,最后求和即可。

代码:

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-10-03 22:52:17 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-10-04 00:04:43
 */
#include <iostream>
#include <cstdio>

const int maxn = 1000005;
const int mod = 666623333;
typedef long long ll;

bool isprime[maxn];
ll prime_num[maxn], cnt;
ll phi[maxn], last[maxn];

void prime(ll n) {
    for (ll i = 2; i <= n; ++i)
        isprime[i] = true;
    isprime[1] = false;
    for (ll i = 2; i <= n; ++i) {
        if (isprime[i]) prime_num[++cnt] = i;
        for (ll j = 1; j <= cnt && i * prime_num[j] <= n; ++j) {
            isprime[i * prime_num[j]] = false;
            if (i % prime_num[j] == 0) break;
        }
    }
}

int main() {
    ll l, r;
    std :: scanf("%lld %lld", &l, &r);
    prime(maxn - 5);
    for (ll i = l; i <= r; ++i) {
        ll idx = i - l;
        phi[idx] = last[idx] = i;
    }

    for (ll i = 1; i <= cnt && prime_num[i] * prime_num[i] <= r; ++i) {
        ll p = prime_num[i];
        for (ll j = l / p * p + ((l % p) ? p : 0); j <= r; j += p) {
            ll idx = j - l;
            phi[idx] /= p;
            phi[idx] *= p - 1;
            while (last[idx] % p == 0)
                last[idx] /= p;
        }
    }
    
    for (ll i = l; i <= r; ++i) {
        ll idx = i - l;
        if (last[idx] > 1) {
            phi[idx] /= last[idx];
            phi[idx] *= (last[idx] - 1);
        }
    }

    ll ans = 0;
    for (ll i = l; i <= r; ++i) {
        ll idx = i - l;
        ans = (ans + (i - phi[idx]) % mod) % mod;
    }

    std :: printf("%lld\n", ans);
    return 0;
}

评测记录

评测记录

posted @ 2020-10-04 00:07  东北小蟹蟹  阅读(67)  评论(0编辑  收藏