[HDU 5608]Function(莫比乌斯反演 + 杜教筛)

题目描述

N23N+2=dNf(d)N^2-3N+2=sum_{d|N} f(d)
i=1Nf(i)sum_{i=1}^{N} f(i)  mod 109+7~mod~10^9+7
1<=T<=5001<=N<=1091<=T<=500\1<=N<=10^9
只有最多55组数据N>106N>10^6

题目分析

f(n)=n23n+2dn,d<nf(d)S(n)=i=1n(i23i+2di,d<if(d))                      =i=1ni23i=1ni+2nk=2nd=1nkf(d)                    =i=1ni23i=1ni+2nk=2nS(nk)                    =n(n+1)(n4)3+2nk=2nS(nk) f(n)=n^2-3n+2-sum_{d|n,d<n}f(d)\ herefore S(n)=sum_{i=1}^n(i^2-3i+2-sum_{d|i,d<i}f(d))\ =sum_{i=1}^ni^2-3sum_{i=1}^ni+2n-sum_{k=2}^nsum_{d=1}^{lfloorfrac nk floor}f(d)\ =sum_{i=1}^ni^2-3sum_{i=1}^ni+2n-sum_{k=2}^nS({lfloorfrac nk floor})\ =frac {n(n+1)(n-4)}3+2n-sum_{k=2}^nS({lfloorfrac nk floor})
如此一来就可以杜教筛了,然而仅仅这样还是会T,于是我们在想一想如何筛出前面一部分的ff
g(n)=n23n+2g(n)=n^2-3n+2,根据莫比乌斯反演
f(n)=dnμ(nd)g(d) herefore f(n)=sum_{d|n}mu(lfloorfrac nd floor)g(d)
于是用Θ(106ln(106))Theta (10^6ln(10^6))筛出前10610^6项就行了
总时间复杂度Θ(106ln(106)+T+5n23)Theta(10^6ln(10^6)+T+5n^{frac 23})

AC code:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
const int MAXN = 1000001;
const int mod = 1e9+7;
const int inv3 = 333333336;

inline int g(int n)
{
	return (1ll * n * n % mod - 3ll * n % mod + 2) % mod;
}

int Prime[MAXN], Cnt, mu[MAXN], f[MAXN];
bool IsnotPrime[MAXN];

void init()
{
	mu[1] = 1;
	for(int i = 2; i < MAXN; ++i)
	{
		if(!IsnotPrime[i]) Prime[++Cnt] = i, mu[i] = -1;
		for(int j = 1; j <= Cnt && i * Prime[j] < MAXN; ++j)
		{
			IsnotPrime[i * Prime[j]] = 1;
			if(i % Prime[j] == 0)
			{
				mu[i * Prime[j]] = 0;
				break;
			}
			mu[i * Prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < MAXN; ++i)
		for(int j = i; j < MAXN; j+=i)
			f[j] = (f[j] + 1ll * mu[j/i] * g(i) % mod) % mod;
	for(int i = 1; i < MAXN; ++i) f[i] = (f[i-1] + f[i]) % mod;
}
map<int,int>F;
inline int solve(int n)
{
	if(n < MAXN) return f[n];
	if(F.count(n)) return F[n];
	int ret = (1ll * n * (n+1) % mod * (n-4) % mod * inv3 % mod + 2ll*n%mod) % mod;
	for(int i = 2, j; i <= n; i=j+1)
	{
		j = n/(n/i);
		ret = (ret - 1ll * (j-i+1) * solve(n/i) % mod) % mod;
	}
	return F[n]=ret;
}

int main()
{
	init();
	int T, n;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d", &n);
		printf("%d
", (solve(n)+mod)%mod);
	}
}
原文地址:https://www.cnblogs.com/Orz-IE/p/12039462.html