【NOI2016】循环之美(数论,莫比乌斯反演,杜教筛)

题面

🔗
在这里插入图片描述
在这里插入图片描述

题解

我们对着屏幕想了好久,终于写出了这个答案的等价式子:
a n s = ∑ x = 1 n ∑ y = 1 m [ ( x , y ) = 1 ] [ ( y , k ) = 1 ] ans=\sum_{x=1}^{n}\sum_{y=1}^{m}[(x,y)=1][(y,k)=1] ans=x=1ny=1m[(x,y)=1][(y,k)=1]

然后,我们对着第一个互质进行莫反。

快速莫反的一个方法:记牢这个式子, ∑ i ∣ n μ ( i ) = [ n = 1 ] \sum_{i|n}\mu(i)=[n=1] inμ(i)=[n=1],然后代入原式。

不难得到
a n s = ∑ x = 1 n ∑ y = 1 m [ ( y , k ) = 1 ] ∑ d ∣ ( x , y ) μ ( d ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ⋅ ⌊ n d ⌋ ⋅ ∑ i = 1 m / d [ ( d i , k ) = 1 ] = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) [ ( d , k ) = 1 ] ⋅ ⌊ n d ⌋ ⋅ ∑ i = 1 m / d [ ( i , k ) = 1 ] ans=\sum_{x=1}^{n}\sum_{y=1}^{m}[(y,k)=1]\sum_{d|(x,y)}\mu(d)\\ =\sum_{d=1}^{\min(n,m)}\mu(d)\cdot\lfloor\frac{n}{d}\rfloor\cdot\sum_{i=1}^{m/d}[(di,k)=1]\\ =\sum_{d=1}^{\min(n,m)}\mu(d)[(d,k)=1]\cdot\lfloor\frac{n}{d}\rfloor\cdot\sum_{i=1}^{m/d}[(i,k)=1] ans=x=1ny=1m[(y,k)=1]d(x,y)μ(d)=d=1min(n,m)μ(d)dni=1m/d[(di,k)=1]=d=1min(n,m)μ(d)[(d,k)=1]dni=1m/d[(i,k)=1]

于是我们发现, ⌊ n d ⌋ \lfloor\frac{n}{d}\rfloor dn ⌊ m d ⌋ \lfloor\frac{m}{d}\rfloor dm 都只有 n , m \sqrt n,\sqrt m n ,m 种值,可以数论分块。

那么接下来解决的就是快速求出下面两个函数:
F ( n , k ) = ∑ i = 1 n [ ( i , k ) = 1 ] G ( n , k ) = ∑ i = 1 n μ ( i ) [ ( i , k ) = 1 ] F(n,k)=\sum_{i=1}^{n}[(i,k)=1]\\ G(n,k)=\sum_{i=1}^{n}\mu(i)[(i,k)=1] F(n,k)=i=1n[(i,k)=1]G(n,k)=i=1nμ(i)[(i,k)=1]

由于这道题通篇出现 k k k 的地方也就是判断与另一个数是否互质,所以我们完全可以把 k k k 的重复质因子都消去,令 k = p 0 p 1 p 2 . . . k=p_0p_1p_2... k=p0p1p2... ,这样就满足 ( p , k / p ) = 1 (p,k/p)=1 (p,k/p)=1 ,方便进行 F F F G G G 的递归计算:
F ( n , k ) = ∑ i = 1 n [ ( i , k ) = 1 ] = ∑ i = 1 n [ ( i , k / p 0 ) = 1 ] [ ( i , p 0 ) = 1 ] = ∑ i = 1 n [ ( i , k / p 0 ) = 1 ] − ∑ i = 1 n / p 0 [ ( i ( p 0 ) , k / p 0 ) = 0 ] = F ( n , k / p 0 ) − F ( n / p 0 , k / p 0 )     G ( n , k ) = ∑ i = 1 n μ ( i ) [ ( i , k ) = 1 ] = ∑ i = 1 n μ ( i ) [ ( i , k / p 0 ) = 1 ] [ ( i , p 0 ) = 1 ] = ∑ i = 1 n μ ( i ) [ ( i , k / p 0 ) = 1 ] − ∑ i = 1 n / p 0 μ ( i p 0 ) [ ( i , k / p 0 ) = 0 ] = ∑ i = 1 n μ ( i ) [ ( i , k / p 0 ) = 1 ] − μ ( p 0 ) ∑ i = 1 n / p 0 [ ( i , p 0 ) = 1 ] μ ( i ) [ ( i , k / p 0 ) = 0 ] = ∑ i = 1 n μ ( i ) [ ( i , k / p 0 ) = 1 ] + ∑ i = 1 n / p 0 μ ( i ) [ ( i , k ) = 0 ] = G ( n , k / p 0 ) + G ( n / p 0 , k ) F(n,k)=\sum_{i=1}^n [(i,k)=1]=\sum_{i=1}^n [(i,k/p_0)=1][(i,p_0)=1]\\ =\sum_{i=1}^n[(i,k/p_0)=1]-\sum_{i=1}^{n/p_0}[(i(p_0),k/p_0)=0]\\ =F(n,k/p_0)-F(n/p_0,k/p_0)\\ ~\\ ~\\ G(n,k)=\sum_{i=1}^{n}\mu(i)[(i,k)=1]=\sum_{i=1}^{n}\mu(i)[(i,k/p_0)=1][(i,p_0)=1]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]-\sum_{i=1}^{n/p_0}\mu(ip_0)[(i,k/p_0)=0]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]-\mu(p_0)\sum_{i=1}^{n/p_0}[(i,p_0)=1]\mu(i)[(i,k/p_0)=0]\\ =\sum_{i=1}^n\mu(i)[(i,k/p_0)=1]+\sum_{i=1}^{n/p_0}\mu(i)[(i,k)=0]\\ =G(n,k/p_0)+G(n/p_0,k) F(n,k)=i=1n[(i,k)=1]=i=1n[(i,k/p0)=1][(i,p0)=1]=i=1n[(i,k/p0)=1]i=1n/p0[(i(p0),k/p0)=0]=F(n,k/p0)F(n/p0,k/p0)  G(n,k)=i=1nμ(i)[(i,k)=1]=i=1nμ(i)[(i,k/p0)=1][(i,p0)=1]=i=1nμ(i)[(i,k/p0)=1]i=1n/p0μ(ip0)[(i,k/p0)=0]=i=1nμ(i)[(i,k/p0)=1]μ(p0)i=1n/p0[(i,p0)=1]μ(i)[(i,k/p0)=0]=i=1nμ(i)[(i,k/p0)=1]+i=1n/p0μ(i)[(i,k)=0]=G(n,k/p0)+G(n/p0,k)

它们的边界都很好想, F ( 0 , k ) = 0 , F ( n , 1 ) = n , G ( 0 , k ) = 0 F(0,k)=0,F(n,1)=n,G(0,k)=0 F(0,k)=0,F(n,1)=n,G(0,k)=0 ,但是
G ( n , 1 ) = ∑ i = 1 n μ ( i ) G(n,1)=\sum_{i=1}^n\mu(i) G(n,1)=i=1nμ(i)

我们可以用杜教筛:

(会杜教筛的跳过)
S ( n ) = ∑ i = 1 n μ ( i ) S(n)=\sum_{i=1}^n\mu(i) S(n)=i=1nμ(i) ,因为
∑ i ∣ x μ ( x / i ) = [ x = 1 ] \sum_{i|x}\mu(x/i)=[x=1] ixμ(x/i)=[x=1]

所以把这个进行求和:
∑ i = 1 n ∑ j ∣ i μ ( i / j ) = ∑ i = 1 n [ i = 1 ] = 1 \sum_{i=1}^{n}\sum_{j|i}\mu(i/j)=\sum_{i=1}^{n}[i=1]=1 i=1njiμ(i/j)=i=1n[i=1]=1

简单变一下式:
∑ j = 1 n ∑ j ∣ i n μ ( i / j ) = ∑ j = 1 n ∑ i = 1 n / j μ ( i ) = 1 ∑ i = 1 n μ ( i ) + ∑ j = 2 n ∑ i = 1 n / j μ ( i ) = 1 S ( n ) = 1 − ∑ j = 2 n S ( n / j ) \sum_{j=1}^n\sum_{j|i}^n\mu(i/j)=\sum_{j=1}^{n}\sum_{i=1}^{n/j}\mu(i)=1\\ \sum_{i=1}^n\mu(i)+\sum_{j=2}^n\sum_{i=1}^{n/j}\mu(i)=1\\ S(n)=1-\sum_{j=2}^nS(n/j) j=1njinμ(i/j)=j=1ni=1n/jμ(i)=1i=1nμ(i)+j=2ni=1n/jμ(i)=1S(n)=1j=2nS(n/j)

根据这个式子,我们部分记忆化递归、部分预处理,由于数论分块的性质,总的时间复杂度不满 O ( n 2 / 3 ) O(n^{2/3}) O(n2/3)

总时间复杂度为 O ( n ⋅ λ ( k ) + n 2 / 3 ) O(\sqrt n\cdot\lambda(k)+n^{2/3}) O(n λ(k)+n2/3)

CODE

我在杜教筛的部分,一不小心取模了,后来又没删完,把 “-” 保留成了 “+1000000007-” ,居然能拿 70+,我还以为是个很小的错误。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 1000000007;
int n,m,s,o,k;
int qkpow(int a,int b) {
	int res = 1;
	while(b > 0) {
		if(b & 1) res = res *1ll* a % MOD;
		a = a*1ll*a % MOD; b >>= 1;
	}return res;
}
const int MXN = 10000000;
int p[MXN+5],mu[MXN+5],cnp;
bool f[MXN+5];
int sieve(int n) {
	mu[1] = 1; f[1] = 1;
	for(int i = 2;i <= n;i ++) {
		if(!f[i]) p[++ cnp] = i,mu[i] = -1;
		for(int j = 1;j <= cnp && i*p[j] <= n;j ++) {
			f[i*p[j]] = 1;
			if(i % p[j] == 0) {mu[i*p[j]]=0;break;}
			mu[i*p[j]] = -mu[i];
		}
	}
	for(int i = 1;i <= n;i ++) mu[i] += mu[i-1];
	return 0;
}
const int useless = sieve(MXN);
map<int,int> mp;
int S(int n) {
	if(n <= 0) return 0;
	if(n <= MXN) return mu[n];
	if(mp.count(n)) return mp[n];
	int ans = 1;
	for(int i = 2;i <= n;i ++) {
		int r = n/(n/i);
		ans -= S(n/i) *1ll* (r-i+1);
		i = r;
	}
	return mp[n] = ans;
}
int pk[2005],cnk;
map<pair<int,int>,LL> mpf;
LL F(int n,int id) {
	if(n == 0) return 0;
	if(n == 1) return 1;
	if(id == 0) return n;
	if(mpf.find({n,id}) != mpf.end()) return mpf[{n,id}];
	LL ans = F(n,id-1) - F(n/pk[id],id-1);
	return mpf[{n,id}] = ans;
}
map<pair<int,int>,LL> mpg;
LL G(int n,int id) {
	if(n == 0) return 0;
	if(n == 1) return 1;
	if(id == 0) return S(n);
	if(mpg.find({n,id}) != mpg.end()) return mpg[{n,id}];
	LL ans = G(n,id-1) + G(n/pk[id],id);
	return mpg[{n,id}] = ans;
}
int main() {
	n = read(); m = read(); k = read();
	int kk = k;
	for(int i = 1;p[i]*p[i] <= kk;i ++) {
		if(kk % p[i] == 0) {
			pk[++ cnk] = p[i];
			while(kk % p[i] == 0) kk /= p[i];
		}
	}
	if(kk > 1) pk[++ cnk] = kk;
	LL ans = 0;
	for(int i = 1;i <= n && i <= m;i ++) {
		int r = min(n/(n/i),m/(m/i));
		ans += (G(r,cnk) - G(i-1,cnk)) *1ll* (n/i) *1ll* F(m/i,cnk);
		i = r;
	}
	AIput(ans,'\n');
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值