簡單的數學題
題目連接
https://www.luogu.org/problemnew/show/P3768
題目描述
輸入一個正整數和.且爲質數.
計算對取模.
題解
方法一.暴力推公式
- 考慮把枚舉
原式= - 莫比烏斯反演處理後邊的式子
設
故
因此 - 合併兩部分
原式=
轉而枚舉.我們得到:
原式
我們發現是常見的狄利克雷卷積.
因此原式
化簡到這一步的時候,前面部分可以分塊計算,後面的部分要快速計算前綴和.
於是我們想到了杜教篩:
記,找到積性函數與它做卷積使得的前綴和可以快速求出,而的前綴和也可以快速求出.
記
根據杜教篩公式.
寫到這就完了.
方法二.卷積
根據公式:
因此
同樣也得到了我們的式子,是不是推導方法簡單多了?
遇見的時候,多想想是不是可以用卷積來做?
可能會減少很多不必要的推導過程.
代碼
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(LL i = a;i <= b;++i)
const int N = 1e7;
typedef long long LL;
LL n,p;
LL phi[N+10];
int prime[N+10],zhi[N+10],low[N+10],pcnt;
LL mod_pow(LL x,LL n) {
LL res = 1;
while(n) {
if(n&1) res = res * x % p;
x = x * x % p;
n >>= 1;
}
return res;
}
LL inv6,inv2,inv4;
void sieve() {
pcnt = 0;
low[1] = phi[1] = zhi[1] = 1;
rep(i,2,N) {
if(!zhi[i]) {
phi[i] = i-1;
prime[pcnt++] = i;
low[i] = i;
}
for(LL j = 0;j < pcnt && prime[j]*i <= N;++j) {
zhi[i*prime[j]] = 1;
if(i % prime[j] == 0) {
low[i*prime[j]] = low[i] * prime[j];
if(i == low[i]) {
phi[i*prime[j]] = phi[i]*prime[j];
}
else {
phi[i*prime[j]] = phi[i/low[i]] * phi[low[i]*prime[j]];
}
break;
}
else{
low[i*prime[j]] = prime[j];
phi[i*prime[j]] = phi[i] * phi[prime[j]];
}
}
}
}
LL sum3(LL n) {
n %= p;
LL res = n*(n+1)%p*(2*n+1)%p*inv6%p;
return res;
}
LL sum4(LL n) {
n %= p;
LL x = n*(n+1)%p;
return x*x%p*inv4%p;
}
std::unordered_map<LL,LL> vis,rec;
LL F(LL n) {
if(n <= N) return phi[n];
if(vis[n]) return rec[n];
LL res = sum4(n);
for(LL x = 2,last;x <= n;x = last) {
last = n/(n/x)+1;
res = (res - ((sum3(last-1)-sum3(x-1)+p)%p*F(n/x)%p) + p) % p;
}
vis[n] = 1;
return rec[n] = res;
}
signed main() {
sieve();
std::ios::sync_with_stdio(false);
std::cin >> p >> n;
inv6 = mod_pow(6,p-2);
inv4 = mod_pow(4,p-2);
inv2 = mod_pow(2,p-2);
rep(i,1,N) phi[i] = ((phi[i]*i%p*i%p) + phi[i-1]) % p;
LL last,ans = 0;
for(LL x = 1;x <= n;x = last+1) {
last = n/(n/x);
LL y = n/x%p;
LL z = (1+y)*(y)/2%p;
ans = (ans + (z*z%p*((F(last)-F(x-1)+p)%p))) % p;
}
std::cout << ans << std::endl;
return 0;
}