【题解】BZOJ-4176 Lucas的数论

Lucas的数论

Description

  • 给定整数 n,求

    [i=1nj=1nd(ij)]mod(109+7)

  • 对于 100% 的数据 n109

Solution

i=1nj=1nd(ij)=i=1nj=1nxiyj[gcd(x,y)=1]=x=1ny=1n[gcd(x,y)=1]i=1n[xi]j=1n[yj]=x=1ny=1n[gcd(x,y)=1]nxny=d=1nμ(d)x=1n[dx]nxy=1n[dy]ny=d=1nμ(d)(i=1ndndi)2(1)=d=1nμ(d)(i=1ndd(i))2(2)

(1) 中有两层整除分块。

μ 用杜教筛,阈值为 106

预处理 d 的前缀和。

k 为阈值,当 ndk 时用 (2) 的公式,以预处理过的 d 的前缀和来计算;当 nd>k 时用 (1) 中的整除分块计算。

那么预处理是 O(k) 的。

第一层整除分块中,若 ndk,即 dnk,那么就会用 d 的前缀和 O(1) 计算,这一段是 O(nnk)O(n) 的。

第一层整除分块中,若 nd>k,即 d<nk,那么就会用整除分块来计算,一共是 d=1nknd,即

0nknxdx=2nk

也就是 O(nk) 的。

综上,总时间复杂度为 O(k+n+nk)k 大约取 n23 时最优,和杜教筛一样。

Code

// 18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <unordered_map>x
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

const int MAXN = 1e6 + 5;
const int N = 1e6;
const int MOD = 1e9 + 7;

int p[MAXN], mu[MAXN], sum_mu[MAXN], d[MAXN], sum_d[MAXN], num[MAXN];
bool vis[MAXN];

void pre()
{
	mu[1] = sum_mu[1] = d[1] = sum_d[1] = 1;
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
		{
			p[++p[0]] = i;
			mu[i] = -1;
			d[i] = 2;
			num[i] = 1;
		}
		for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
		{
			vis[i * p[j]] = true;
			if (i % p[j] == 0)
			{
				mu[i * p[j]] = 0;
				d[i * p[j]] = d[i] / (num[i] + 1) * (num[i] + 2);
				num[i * p[j]] = num[i] + 1;
				break;
			}
			mu[i * p[j]] = mu[i] * mu[p[j]];
			d[i * p[j]] = d[i] * d[p[j]];
			num[i * p[j]] = 1;
		}
		sum_mu[i] = sum_mu[i - 1] + mu[i];
		sum_d[i] = (sum_d[i - 1] + d[i]) % MOD;
	}
}

unordered_map<int, int> dp_mu;

int sublinear_mu(int n)
{
	if (n <= N)
	{
		return sum_mu[n];
	}
	if (dp_mu.find(n) != dp_mu.end())
	{
		return dp_mu[n];
	}
	int res = 1;
	for (int l = 2, r; l <= n; l = r + 1)
	{
		int k = n / l;
		r = n / k;
		res = (res - (ll)(r - l + 1) * sublinear_mu(k) % MOD + MOD) % MOD;
	}
	return dp_mu[n] = res;
}

int getsum_mu(int l, int r)
{
	return (sublinear_mu(r) - sublinear_mu(l - 1) + MOD) % MOD;
}

int getsum_d(int n)
{
	if (n <= N)
	{
		return sum_d[n];
	}
	int res = 0;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		int k = n / l;
		r = n / k;
		res = (res + (ll)(r - l + 1) * k % MOD) % MOD;
	}
	return res;
}

int block(int n)
{
	int res = 0;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		int k = n / l;
		r = n / k;
		int tmp = getsum_d(k);
		res = (res + (ll)getsum_mu(l, r) * tmp % MOD * tmp % MOD) % MOD;
	}
	return res;
}

int main()
{
	pre();
	int n;
	scanf("%d", &n);
	printf("%d\n", block(n));
	return 0;
}
posted @   mango09  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
-->
点击右上角即可分享
微信分享提示