HDU 6868:Absolute Math(莫比乌斯反演 + 离线暴力)

在这里插入图片描述


f ( n ) = ∑ d ∣ n μ ( d ) 2 f(n) = \displaystyle\sum_{d|n}\mu(d)^2 f(n)=dnμ(d)2,根据 μ ( d ) \mu(d) μ(d) 的定义,当 d d d 的质因子的幂次不高于一次时有贡献,设 d d d 的质因子共有 w ( d ) w(d) w(d) 种, f ( n ) = ∑ d ∣ n μ ( d ) 2 = ∑ i = 0 w ( d ) C ( w ( d ) , i ) = 2 w ( d ) f(n) = \displaystyle\sum_{d|n}\mu(d)^2 = \sum_{i=0}^{w(d)}C(w(d),i)=2^{w(d)} f(n)=dnμ(d)2=i=0w(d)C(w(d),i)=2w(d)

容易发现 f ( n ) f(n) f(n) 是一个积性函数。 ∑ i = 1 m f ( n i ) = f ( n ) ∑ i = 1 m f ( i ) f ( g c d ( n , i ) ) \displaystyle\sum_{i = 1}^mf(ni)=f(n)\sum_{i=1}^m\frac{f(i)}{f(gcd(n,i))} i=1mf(ni)=f(n)i=1mf(gcd(n,i))f(i)。 接下来推导式子:
∑ i = 1 m f ( i ) f ( g c d ( n , i ) ) = ∑ d ∣ n 1 f ( d ) ∑ i = 1 ⌊ m d ⌋ f ( i d ) ∗ [ g c d ( n d , i ) = 1 ] \sum_{i=1}^m\frac{f(i)}{f(gcd(n,i))}=\sum_{d|n}\frac{1}{f(d)}\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}f(id)*[gcd(\frac{n}{d},i) = 1] i=1mf(gcd(n,i))f(i)=dnf(d)1i=1dmf(id)[gcd(dn,i)=1]
∑ d ∣ n 1 f ( d ) ∑ t ∣ n d μ ( t ) ∑ i = 1 ⌊ m t ∗ d ⌋ f ( i ∗ t ∗ d ) \sum_{d|n}\frac{1}{f(d)}\sum_{t|\frac{n}{d}}\mu(t)\sum_{i=1}^{\lfloor\frac{m}{t*d}\rfloor}f(i*t*d) dnf(d)1tdnμ(t)i=1tdmf(itd)
T = t ∗ d T = t*d T=td ∑ T ∣ n ∑ d ∣ T 1 f ( d ) ∗ μ ( T d ) ∑ i = 1 ⌊ m T ⌋ f ( i ∗ T ) \sum_{T|n}\sum_{d|T}\frac{1}{f(d)}*\mu(\frac{T}{d})\sum_{i=1}^{\lfloor\frac{m}{T}\rfloor}f(i*T) TndTf(d)1μ(dT)i=1Tmf(iT)
g ( T ) = ∑ d ∣ T 1 f ( d ) ∗ μ ( T d ) g(T) = \displaystyle\sum_{d|T}\frac{1}{f(d)}*\mu(\frac{T}{d}) g(T)=dTf(d)1μ(dT),得到:
∑ T ∣ n g ( T ) ∑ i = 1 ⌊ m T ⌋ f ( i ∗ T ) \sum_{T|n}g(T)\sum_{i=1}^{\lfloor\frac{m}{T}\rfloor}f(i*T) Tng(T)i=1Tmf(iT)
观察 g ( T ) g(T) g(T) g ( T ) g(T) g(T) 显然是一个积性函数,可以用欧拉筛预处理。
g ( p ) g(p) g(p) 显然为 1 2 − 1 = − 1 2 \frac{1}{2} - 1=-\frac{1}{2} 211=21
g ( p k ) g(p^k) g(pk) 显然为 1 2 − 1 2 = 0 \frac{1}{2}-\frac{1}{2} = 0 2121=0
由此可以观察到, g ( T ) g(T) g(T) 只有在 T T T 的最高幂次不大于 1 的情况下不为 0,对于 n n n 的因子 T,只需要枚举其质因子的组合即可,由于 n ≤ 1 0 7 n \leq 10^7 n107,枚举质因子复杂度约为 8 ∗ 2 8 8*2^8 828

考虑离线暴力计算 ∑ i = 1 ⌊ m T ⌋ f ( i ∗ T ) \displaystyle\sum_{i=1}^{\lfloor\frac{m}{T}\rfloor}f(i*T) i=1Tmf(iT),将每一组询问枚举质因子组合并将子询问维护到数列中,对子询问排序离线计算。 每种质因子组合 p 最多计算一次 m p \frac{m}{p} pm,复杂度和调和级数比较接近,约为 n log ⁡ n n\log n nlogn

总复杂度为 O ( n log ⁡ n + T ( log ⁡ n + 8 ∗ 2 8 ) ) O(n \log n + T(\log n +8*2^8)) O(nlogn+T(logn+828))


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
bool ispri[maxn];
int f[maxn], g[maxn], inv2 = (mod + 1) / 2, n ,m, pri[maxn], t, mindiv[maxn], ans[maxn];
vector<int> p;
struct node {
	int n, T, m, id;
	node() {}
	node(int ni,int Ti,int mi,int idi) {
		n = ni;
		T = Ti;
		m = mi;
		id = idi;
	}
	bool operator < (const node &rhs) const{
		if (T != rhs.T) return T < rhs.T;
		return m < rhs.m;
	}
};
vector<node> q;
void sieve(int n) {
    ispri[0] = ispri[1] = true;
    pri[0] = 0; f[1] = 1, g[1] = 1, mindiv[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!ispri[i]) {
            pri[++pri[0]] = i;
            f[i] = 2;
            g[i] = mod - inv2;
            mindiv[i] = i;
        }
        for (int j = 1; j <= pri[0] && i * pri[j] <= n; j++) {
            ispri[i * pri[j]] = true;
            if (i % pri[j] == 0) {
                f[i * pri[j]] = f[i];
                mindiv[i * pri[j]] = mindiv[i];
                break;
            } else {
                mindiv[i * pri[j]] = pri[j];
                f[i * pri[j]] = 2 * f[i];
                g[i * pri[j]] = 1ll * g[i] * g[pri[j]] % mod;
            }
        }
    }
}
int main() {
    sieve(10000000);
    scanf("%d",&t);
    int id = 0;
    while (id++, t--) {
        scanf("%d%d",&n,&m);
        p.clear();
        int tmp = n;
        while (tmp != 1) {
        	if (mindiv[tmp] != mindiv[tmp / mindiv[tmp]])
        		p.push_back(mindiv[tmp]);
        	tmp /= mindiv[tmp];
		}
		int v = p.size();
		for (int i = 0; i < (1 << v); i++) {
			int T = 1;
			for (int j = 0; j < v; j++)
				if ((i >> j) & 1) T *= p[j];
			q.push_back(node(n,T,m / T * T,id));
		}
    }
    sort(q.begin(),q.end());
    int T = 0, w = 0, j = 0;
    for (auto it : q) {
    	if (it.T != T) {
    		T = it.T, j = it.T, w = 0;
		}
		for (; j <= it.m; j += T)
			w = (w + f[j]) % mod;
		ans[it.id] = (ans[it.id] + 1ll * g[T] * w * f[it.n] % mod) % mod;
	}
	for (int i = 1; i < id; i++)
		printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值