[NOI2016] 循环之美

\(\text{Solution}\)

容易发现答案为

\[\sum_{x = 1}^n\sum_{y = 1}^m[\gcd(x,y) = 1][\gcd(y,k) = 1] \]

\[=\sum_{x = 1}^n\sum_{y = 1}^m\sum_{d|\gcd(x,y)}\mu(d)\sum_{g|\gcd(y,k)}\mu(g) \]

考虑枚举\(\gcd(x,y)\)

\[=\sum_{i = 1}^{\min(n,m)}\mu(i)[i \perp k]\sum_{i | x}\sum_{j = 1}^{\lfloor \frac mi \rfloor}[j \perp k] \]

\(g(n) = \sum_{i = 1}^n [i \perp k]\)

\[=\sum_{i = 1}^{\min(n,m)}\mu(i)[i \perp k]\lfloor\dfrac ni\rfloor g(\lfloor \frac mi \rfloor) \]

如何求\(g(n)\)
对于互质有一个性质
\(a\perp b\)时, \((a + c * b) \perp b\)(易证)。
所以\(g(n) = \lfloor\dfrac{n}{k}\rfloor * g(k) + g(n\mod k)\)
现在只需考虑如何求\(\sum_{i = 1}^{\min(n,m)}\mu(i)[i \perp k]\)
\(f(n) = \sum_{i = 1}^n\mu(i)[i \perp k]\)
那么

\[f(n) = \sum_{i = 1}^n f(\lfloor \frac{n}{i} \rfloor)[i \perp k] - \sum_{i = 2}^n f(\lfloor \frac{n}{i} \rfloor)[i \perp k] \]

发现

\[\sum_{i = 1}^n f(\lfloor \frac{n}{i} \rfloor)[i \perp k] \]

\[=\sum_{i = 1}^n \sum_{j = 1}^{\lfloor \frac{n}{i} \rfloor}\mu(j)[i * j \perp k] \]

\(T = i * j\),枚举\(T\)

\[=\sum_{T = 1}^n [T \perp k] \sum_{d|T}\mu(d) \]

\[=\sum_{T = 1}^n [T \perp k][T = 1] \]

\[=1 \]

最后柿子就是

\[f(n) = 1 - \sum_{i = 2}^n f(\lfloor \frac{n}{i} \rfloor)[i \perp k] \]

就可以愉快地杜教筛了,时间复杂度\(O(n^{\frac{2}{3}})\)

\(\text{Code}\)

#include<cstdio>
#include<map>
#include<algorithm>
#define LL long long
using namespace std;
const int M = 2e6;
int n,m,k,vis[M + 5],p[M + 5],tot;
LL f[M + 5],cop[M + 5],mu[M + 5],g[M + 5];

map<int,LL> fm;
LL getG(int x) {return (LL)(x / k) * g[k] + g[x % k];}
int gcd(int x,int y){return y == 0 ? x : gcd(y,x % y);}
void init()
{
	mu[1] = cop[1] = 1;
	for (int i = 2; i <= M; i++)
	{
		if (!vis[i]) p[++tot] = i,mu[i] = -1,cop[i] = (k % i ? 1 : 0);
		for (int j = 1; j <= tot && p[j] * i <= M; j++)
		{
			vis[p[j] * i] = 1,cop[p[j] * i] = cop[i] * cop[p[j]];
			if (i % p[j] == 0) break;
			mu[p[j] * i] = -mu[i];
		}
	}
	for (int i = 1; i <= k; i++) g[i] = g[i - 1] + cop[i];
	for (int i = 1; i <= M; i++) f[i] = f[i - 1] + cop[i] * mu[i];
}
LL gets(int x)
{
	if (x <= M) return f[x];
	if (fm[x]) return fm[x];
	LL res = 1LL;
	for (int l = 2,r; l <= x; l = r + 1)
		r = x / (x / l),res -= gets(x / l) * (getG(r) - getG(l - 1));
	return fm[x] = res;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k),init(); LL ans = 0;
	for (int l = 1,r; l <= min(n,m); l = r + 1)
		r = min(n / (n / l),m / (m / l)),ans += (LL)(gets(r) - gets(l - 1)) * (n / l) * getG(m / l);
	printf("%lld\n",ans);
}
posted @ 2022-03-09 21:32  RiverSheep  阅读(46)  评论(0)    收藏  举报