1. 程式人生 > 實用技巧 >題解 P3601 【簽到題】

題解 P3601 【簽到題】

題目連結

Solution 簽到題

題目大意:定義\(qiandao(n)=\sum_{i=1}^{n}[gcd(i,n)\neq1]\),求\(\sum_{i=l}^rqiandao(i)\),其中\(l,r\leq 10^{12},r-l \leq 10^6\)

數論


分析:首先\(qiandao(n)=n-\varphi(n)\)

所以我們考慮咋把\(\sum_{i=l}^{r}\varphi(i)\)求出來

\([l,r]\)內每個數利用唯一分解定理分解,由於一個數\(n\)分解後的指數之和不會超過\(log_2n\),所以我們只需要求出這個數有哪些質因子,然後暴力求\(\varphi\)

即可

我們篩出\([1,\sqrt{10^{12}}=10^6]\)內的所有質數,對於每個質數,將它在\([l,r]\)內的所有倍數打上標記

根據神尤拉給出的結論,這個的時間複雜度是\(nlogn\)級別的

於是我們得到了\([l,r]\)內每個數\(x\),小於\(\sqrt{x}\)的質因子,將它們的冪次全部除掉,得到的數如果不是\(1\),那它一定是一個大於\(\sqrt{x}\)的質因子,我們得到了\(x\)的所有質因子

然後愉快的求\(\varphi\)即可

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100,mod = 666623333;
inline ll add(ll a,ll b){return (a + b) % mod;}
bool vis[maxn];
vector<int> pri;
inline void sieve(){
	for(int i = 2;i < maxn;i++){
		if(!vis[i])pri.push_back(i);
		for(int x : pri){
			if(1ll * x * i >= maxn)break;
			vis[i * x] = 1;
			if(i % x == 0)break;
		}
	}
}
ll l,r,ans;
vector<int> vec[maxn];
int main(){
	sieve();
	cin >> l >> r;
	for(int p : pri){
		for(ll now = ceil((long double)l / p) * p;now <= r;now += p)
			vec[now - l].push_back(p);
	}
	for(ll now = l;now <= r;now++){
		ll tmp = now,phi = 1;
		for(int p : vec[now - l]){
			phi *= p - 1;
			tmp /= p;
			while((tmp % p) == 0){
				phi *= p;
				tmp /= p;
			}
		}
		if(tmp != 1)phi *= tmp - 1;
		ans = add(ans,now - phi);
	}
	cout << ans << '\n';
	return 0;
}