HDU 6134 Battlestation Operational 莫比乌斯反演 + 数学推导

16 篇文章 0 订阅
3 篇文章 0 订阅

传送门:HDU 6134

题意:给定n,求:

思路:

以上转自:点击打开链接


上面的推导过程中最重要也是最不好理解的就是‘重要发现’那里,我们可以举个例子解释,假设n = 12

那么等式右边就为(12/1 + 12/2 + 12/ 3 + 12/ 4 + 12/5 + 12/6 + 12/7 + 12/8 + 12/9 + 12/10 + 12/11 + 12/12)

然后我们将上面每个分数的分子分母同时除以其gcd得

(12/1 + 6/1 + 4/ 1 + 3/1 + 12/5 + 2/1 + 12/7 + 3/2 + 4/3 + 6/5 + 12/11 + 1/1)

你会惊奇的发现这就是等式左边求∑的展开式,而从左边转化成右边,我们也可以看成是∑中每一项分子分母都同时乘了n/d。


然后问题就是如何求F[n]:
做两个函数 p[i] (表示 i / 1 + i / 2 + ... + i / i 向上取整)和 d[i] (表示 i / 1 + i / 2 + ... + i / i 向下取整)
可以发现规律 是 p[i] = d[i - 1] + i ,因为前一项向下取整后,后一项的向上取整就是每个数的值都加一,所以就是加 i 
这就可以通过前一项的 d[i - 1] 求得 p[i] 了,那么怎么求 d[i] 呢,对于当前得 p[i]来说,每一项都是向上取整得,要求他向下取整得数,那么就要减去那些不能整除 i 的数,那些数是通过向上取整取得的,所以要减去,直接找那些数不好找,但是找 i 的因子容易 ,那么就直接 先把 p[i] 减去 i ,然后再加上 i 的因子个数,就得到 d[i]了
d[i] = p[i] - i + (i 的因子数)
以上思路转自:点击打开链接
容易得到d[i]  = d[i - 1] + (i的因子数)
至此我们求得了F[i] = d[i],然后利用莫比乌斯反演推出的f[i]和F[i]之间关系,我们可以求得f[i],注意这里求f[i]的时候也有小技巧,我们枚举i,然后对i的所有倍数都加一遍i所产生的贡献(类似于求[1...n]因子数的方法),这样是调和级数的复杂度(nlogn)。最后我们再对f[i]求一遍前缀和即为答案。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 1000010;
const int mod = 1e9 + 7;
//cnt[i] := i的因子数 mu[i] := moebius值 f[i]和F[i]分别为上面的定义
int cnt[MAXN], mu[MAXN], F[MAXN], f[MAXN], ans[MAXN];
//求欧拉函数所用 
int phi[MAXN], prime[MAXN];
bool vis[MAXN];
void get_phi()
{
	int tot = 0;
	phi[1] = 1;
	for(int i = 2; i < MAXN; i++)
	{
		if(!vis[i])
		{
			prime[tot++] = i;
			phi[i] = i - 1;
		}
		for(int j = 0; j < tot; j++)
		{
			if(i * prime[j] > MAXN) break;
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0)
			{
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			else
			phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
}
void init()
{
	get_phi();
	mu[1] = 1;
	for(int i = 1; i < MAXN; i++)
	for(int j = i; j < MAXN; j += i)
	cnt[j]++, mu[j] -= (j > i ? mu[i] : 0);//求moebius值及[1...n]因子数
	for(int i = 1; i < MAXN; i++)
	F[i] = (F[i - 1] + cnt[i]) % mod;//求F[i]
	for(int i = 1; i < MAXN; i++)
	for(int j = i; j < MAXN; j += i)
	f[j] = (f[j] + mu[j / i] * F[i]) % mod;//求f[i]
	for(int i = 1; i < MAXN; i++)
	ans[i] = (ans[i - 1] + f[i] + phi[i] - 1) % mod;
}
int main()
{
	int n;
	init();
	while(~scanf("%d", &n))
	printf("%d\n", ans[n]);
 	return 0;
}

最后推荐另一种非常好懂的反演 + 公式推导思路: 点击打开链接
推导过程清晰易懂,就是代码看不太明白。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值