LOJ2085. 「NOI2016」循环之美

定义一个\(\frac{x}{y}\)是“纯循环小数”,当且仅当:化成小数之后,可以写成\(a.\dot{c_1}c_2c_3c_4\dots\dot{c_p}\)的形式。

\(x\in[1,n]\)\(y\in[1,m]\),有多少对\((x,y),x \perp y\)满足\(\frac{x}{y}\)\(k\)进制下是纯循环小数。

\(n,m\le 10^9,k\le 2000\)


分析条件:如果是,当且仅当\(\exist p>0,x\equiv k^px\pmod y\)。也就是\(k\perp y\)

所以题目也就是要求\(\sum_{i=1}^m\sum_{j=1}^n [i\perp j][i\perp k]\)

考虑拆式子。

说在前面:作为数论白痴,下面估的时间复杂度往往是不准确的。如果有大佬路过希望指教一下。

当拆\([i \perp j]\)

经过推导得\(\sum_d [d\perp k]\mu(d)\lfloor\frac{n}{d}\rfloor\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}[i\perp k]\)

\(f(n)=\sum_{i=1}^n[i\perp k]=\lfloor\frac{n}{k}\rfloor\phi(k)+f(n\mod k)\)。可以快速求,不是复杂度瓶颈。

对于\(\sum_d [d\perp k]\mu(d)\lfloor\frac{n}{d}\rfloor f(\lfloor\frac{m}{d}\rfloor)\)整除分块,现在要求前缀和,即\(g(n,k)=\sum_{d=1}^n \mu(d)[d\perp k]\)

一种推法

\([d\perp k]\)拆开推一波式子可以得到\(g(n,k)=\sum_{t|k}\mu(t)\sum_{d=1}^{\lfloor\frac{n}{t}\rfloor}\mu(dt)=\sum_{t|k}\mu(t)^2\sum_{d=1}^{\lfloor\frac{n}{t}\rfloor}\mu(d)[d\perp t]=\sum_{t|k}\mu(t)^2g(\lfloor\frac{n}{t}\rfloor,t)\)。整除分块搞下去。

边界是\(g(n,1)\)\(g(0,k)\)。其中\(g(n,1)\)可以杜教筛求。

时间复杂度(记忆化):\(O(n^{2/3}+(k的因子个数)\sqrt n)\)

另一种推法

\(p\)\(k\)的一个质因数,则\(k\)可以表示成\(p^tq\)的形式。

\(g(n,k)=\sum_{i=1}^n[i\perp q]\mu(i)-\sum_{p|i}[i\perp q]\mu(i)=g(n,q)+g(\lfloor\frac{n}{p^t}\rfloor,k)\)

边界同上,时间复杂度(记忆化):\(O(n^{2/3}+(k的质因子个数)\sqrt n)\)

当拆\([i\perp k]\)

\(f(m,n,k)=\sum_{i=1}^m\sum_{j=1}^n [i\perp j][i\perp k]\)

经过推导得\(f(m,n,k)=\sum_{d|k}\mu(d)f(n,\lfloor\frac{m}{d}\rfloor,d)\)

边界条件\(k=1\),化一下得\(\sum_d \mu(d)\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\)。整除分块,杜教筛算\(\mu\)的前缀和。

这个东西不太会分析,但是实践表明递归的次数不是很多(没有对\(f\)进行记忆化的情况下)。杜教筛记忆化\(\lfloor\frac{n}{d}\rfloor\)\(\lfloor\frac{m}{d}\rfloor\)这样的前缀和,总时间\(O(n^{2/3}+m^{2/3})\)。整除分块的时间复杂度大概是\(O(\sqrt{\max(n,m)}\)。总的时间复杂度不会算……

代码是最后一种做法。


using namespace std;
#include <bits/stdc++.h>
#define ll long long
//#define ll __int128
#define N 6000005
int gcd(int a,int b){
	int k;
	while (b)
		k=a%b,a=b,b=k;
	return a;
}
int n,m,k;
int p[N],np;
bool inp[N];
int mu[N],mnp[N];
int smu[N];
void init(int n=6000000){
	mu[1]=1;
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i,mnp[i]=i,mu[i]=-1;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			mnp[i*p[j]]=p[j];
			if (i%p[j]==0)
				break;
			mu[i*p[j]]=-mu[i];
		}
	}
	for (int i=1;i<=n;++i)
		smu[i]=smu[i-1]+mu[i];
}
const int EMP=-1926081700;
struct memory{
	int n;
	ll v0[N],v1[N];
	void init(int _n){
		n=_n;
		for (int i=1;i*i<=n;++i)
			v0[i]=v1[i]=EMP;
	}
	ll &operator[](int x){return (ll)x*x<=n?v0[x]:v1[n/x];}
} bzn,bzm;
ll S1(int n,memory &bzn){
	if (n<=6000000)
		return smu[n];
	if (bzn[n]!=EMP)
		return bzn[n];
	ll sum=0;
	for (int i=2;i<=n;){
		int d=n/i,j=n/d;
		sum+=(ll)(j-i+1)*S1(d,bzn);
		i=j+1;
	}
	return bzn[n]=1-sum;
}
ll S2(int m,int n,int rev){
	if (rev)
		swap(m,n);
	ll sum=0,pre=0;
	for (int i=1;i<=m && i<=n;){
		int dn=n/i,dm=m/i,j=min(n/dn,m/dm);
		ll tmp=S1(j,n/dn<m/dm?bzn:bzm);
		sum+=(ll)(tmp-pre)*dn*dm;
		pre=tmp;
		i=j+1;
	}
	return sum;
}
ll f(int m,int n,int k,int rev){
	if (m==0 || n==0)
		return 0;
	ll s=0;
	if (k==1){
		s+=S2(m,n,rev);
		return s;
	}
	int i=1;
	for (;i*i<k;++i)
		if (k%i==0){
			if (mu[i]) 
				s+=mu[i]*f(n,m/i,i,rev^1);
			if (mu[k/i]) 
				s+=mu[k/i]*f(n,m/(k/i),k/i,rev^1);
		}
	if (i*i==k && mu[i])
		s+=mu[i]*f(n,m/i,i,rev^1);
	return s;
}
int main(){
	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	init();
	scanf("%d%d%d",&n,&m,&k);
	bzn.init(n);
	bzm.init(m);
	printf("%lld\n",(long long)f(m,n,k,0));
//	printf("%lld\n",cnt);
	return 0;
}
posted @ 2021-01-21 21:51  jz_597  阅读(69)  评论(0编辑  收藏  举报