1. 程式人生 > >BZOJ 4916: 神犇和蒟蒻 杜教篩 數學

BZOJ 4916: 神犇和蒟蒻 杜教篩 數學

我來寫個題解造(騙)福(訪)世(問)人(量)

第一問不會的出門左轉百度μ是啥去

第二問的話顯然答案是等於ni=1iφ(i)的,不知道的出門左轉百度φ計算公式去……

然後考慮那個東西我們用杜教篩搞一下

考慮f(x)=xg(x)=xφ(x)的狄利克雷卷積G(x)=d|xf(xd)g(d)

首先我們可以直接帶入一下

G(t)=tx=1d|xxddφ(d)=d|xxφ(d)

G(t)=tx=1xd|xφ(d)

注意到後面的東西其實是x

所以其實G(t)=tx=1x2=t(t+1)(2t+1)6

然後考慮我們不帶入然後算貢獻

G(t)=tx=1d|xf(d)g

(xd)

=tx=1f(x)nx|dg(dx)這步的話其實就是相當於我枚舉了一個數和乘積然後算另外一個的貢獻

=tx=1ndk=1g(k)

考慮當x=1的時候後面的東西就是g(n)即為所求,剩下的根號分塊遞迴處理即可

最後令S(x)=ni=1iφ(i)

那麼會有S(n)=G(i)ni=2iS(ni)

手寫了hash表並沒有變快qwq

另附n=109時的答案為631887325

// 631887325
#include <map>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm> using namespace std; typedef long long LL; const int N = 5e6+5; const int Max = 5e6; const int inv2 = 500000004; const int inv6 = 166666668; const int mod = 1e9+7; const int mod1 = 5e6+5; const int M = 5 * mod1; int prime[N], cnt, phi[N], head[mod1+3]; bool F[N]; map <int,int
>
mp; struct graph { int next, to, val; graph () {} graph (int _next,int _to,int _val) :next(_next),to(_to), val(_val){} }edge[M]; inline void add(int x,int y) { int temp = x % mod1; edge[++cnt] = graph(head[temp], x, y); head[temp] = cnt; } inline int find(int x) { int temp = x % mod1; for(int i=head[temp];i;i=edge[i].next) if(edge[i].to == x) return edge[i].val; return -1; } inline void init() { phi[1] = 1; register int i, j; for(i=2;i<=Max;++i) { if(!F[i]) { prime[++cnt] = i; phi[i] = i-1; } for(j=1;prime[j]*i<=Max;++j) { F[i*prime[j]] = 1; if(i%prime[j]==0) { phi[i*prime[j]] = phi[i] * prime[j]; break; } phi[i*prime[j]] = phi[i] * (prime[j]-1); } } for(i=1;i<=Max;++i) phi[i] = (long long) i*phi[i]%mod; for(i=1;i<=Max;++i) (phi[i] += phi[i-1]) %= mod; } inline int calc_3(int x) { return (long long)x * (x+1) % mod * (2*x+1) % mod * inv6 % mod; } inline int calc_2(int x,int y) { return (LL)(x+y)%mod*(y-x+1)%mod*inv2%mod; } inline int calc(int x) { if(x <= Max) return phi[x]; int tt = find(x); if(tt != -1) return tt; int last = 0; long long ans = 0; (ans += calc_3(x)) %= mod; for(int i=2;i<=x;i=last+1) { last = x/(x/i); (ans += mod-(LL)calc(x/i)*calc_2(i,last)%mod) %= mod; } add(x, ans); return ans; } int main() { init(); int n; cin >> n; puts("1"); cout << calc(n) << endl; }