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

来源:互联网 发布:windows登录oracle 编辑:程序博客网 时间:2024/06/07 15:18

传送门: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 0x3f3f3f3fusing 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;}elsephi[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;}

最后推荐另一种非常好懂的反演 + 公式推导思路:点击打开链接
推导过程清晰易懂,就是代码看不太明白。。
阅读全文
0 0
原创粉丝点击