BZOJ1101: [POI2007]Zap (莫比乌斯反演)

题意:5e4组询问 给定a,b,d求$$ \sum_{i=1}{a}\sum_{j=1}[gcd(i,j) = d]$$
题解:套路

\[f\left ( d\right ) = \sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j) = d] \]

\[F\left ( n \right )=\sum_{d|n} f(d) = \left \lfloor \frac{a}{n} \right \rfloor\left \lfloor \frac{b}{n} \right \rfloor \]

\[f(d) = \sum_{d|n}\mu(\frac{n}{d})F(n) = \sum_{d|n}\mu(\frac{n}{d})\left \lfloor \frac{a}{n} \right \rfloor \left \lfloor \frac{b}{n} \right \rfloor \]

  然后令t = n/d

\[f(d) = \sum_{t=1}^{min(\left \lfloor \frac{a}{d} \right \rfloor,\left \lfloor \frac{b}{d} \right \rfloor)}\mu(t)\left \lfloor \frac{a}{dt} \right \rfloor\left \lfloor \frac{b}{dt} \right \rfloor \]

  然后整除分块一下 筛出莫比乌斯函数的前缀和 做一个是根号n的复杂度

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

bool vis[50005];
int prim[50005];
int mu[50005];
int sum[50005];
int cnt;

void get_mu(int n)
{
	mu[1] = 1;
	for(int i = 2; i <= n; i++)
	{
		if(!vis[i])
		{
			mu[i] = -1;
			prim[++cnt] = i;
		}
		for(int j = 1; j <= cnt && i * prim[j] <= n; j++)
		{
			vis[i * prim[j]] = 1;
			if(i % prim[j] == 0) break;
			else mu[i * prim[j]] -= mu[i];
		}
	}
	for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}

int main()
{
    get_mu(50000);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int a, b, d;
        scanf("%d%d%d", &a, &b, &d);

        ll ans = 0;
        int len = min(a / d, b / d);
        for(int l = 1, r; l <= len; l = r + 1)
        {
            int f1 = a / d;
            int f2 = b / d;
            r = min(f1 / (f1 / l), f2 / (f2 / l));
            ans += 1LL * (f1 / l) * (f2 / l) * (sum[r] - sum[l - 1]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2019-01-31 03:14  lwqq3  阅读(151)  评论(0编辑  收藏  举报