NOI2016 循环之美

题意

题意

求能用 \(\frac{x}{y}(1\leq x\leq n,1\leq y\leq m)\) 表示的 \(k\) 进制下纯循环小数个数 \(n,m\leq 10^9,k\leq 2000\)

题解

题意相当于 \(\sum\limits_{1\leq x\leq n,1\leq y\leq m}[\gcd(x,y)=1][\gcd(y,k)=1]1\) ,考虑反演。 \[ \mathrm{lcm}(x,y)=\frac{xy}{\gcd(x,y)}=x\cdot\frac{y}{\gcd(x,y)}\\ \begin{align}&\sum_{1\leq x\leq n,1\leq y\leq m}[\gcd(x,y)=1][\gcd(y,k)=1]1\\ =&\sum_{1\leq x\leq n,y|k}\mu(x)\mu(y)\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{\mathrm{lcm}(x,y)}\rfloor\\ =&\sum_{1\leq x\leq n,y|k}\mu(x)\mu(y)\lfloor\frac{n}{x}\rfloor\lfloor\frac{\lfloor\frac{m}{x}\rfloor}{\frac{y}{\gcd(x,y)}}\rfloor\\ \end{align} \] 可以发现\(x\)确定的时候, \(\mu(x),\lfloor\frac{n}{x}\rfloor,\lfloor\frac{m}{x}\rfloor\) 都是确定的,由于 \(y|k\)\(\frac{y}{\gcd(x,y)}\) 仅和 \(\gcd(x,k),y\) 相关而不和 \(x\) 相关。

\(\lfloor\frac{n}{x}\rfloor,\lfloor\frac{m}{x}\rfloor\) 整除分块,则我们需要计算 \(\sum\limits_{l\leq x\leq r}[\gcd(x,k)=y]\mu(x)\)

\[ g(n,k,y)=\sum_{1\leq x\leq n}[\gcd(x,k)=y]\mu(x)\\ f(n,k)=g(n,k,1)\\ g(n,k,y)=\mu(y)f(\lfloor\frac{n}{y}\rfloor,k)\\ f(n,k)=\sum_{x=1}^n\mu(x)-\sum_{i|k,i\not=1}g(n,k,i) \] 第二条正确性显然对吧,,,

第一条的话, 看上去应该是 \(g(n,k,y)=\mu(y)f(\lfloor{\frac{n}{y}}\rfloor,\frac{k}{y})\)。有可能的区别是 \(k\) 某一种质因子,全都出现在 \(y\) 中。如果有\(\geq 2\)个,那么已经 \(\mu(y)=0\) 了。如果只有\(1\)个,那么再来一个质因子就直接 \(=0\) 了,如果是 \(\frac{k}{y}\) 就不会 \(=0\) 所以这个式子就很合理。

\(\mu\) 前缀和可以用杜教筛,\(f\) 可以直接暴力,复杂度为\(O(n^{\frac{1}{2}}d(k))\),预处理 \(\leq n^{\frac{2}{3}}\)\(f\) 就是 \(O(n^{\frac{1}{3}}d(k)+n^{\frac{2}{3}})\)

然后 \(y\) 的部分,可以预处理每个 \(x|k,\mu(x)\not=0,y|k\) 时的 \(\frac{y}{\gcd(x,y)}\)

代码

/*
Author: QAQAutoMaton
Lang: C++
Code: cyclic.cpp
Mail: lk@qaq-am.com
Blog: https://www.qaq-am.com/
*/
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define int long long
#define debug(qaq...) fprintf(stderr,qaq)
#define DEBUG printf("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
#define x first
#define y second
#define unq(a) sort(all(a)),a.erase(unique(all(a)),a.end())
using namespace std;
typedef unsigned uint;
typedef long long ll;
typedef unsigned long long ull;
typedef complex<double> cp;
typedef pair<int,int> pii;
int inf;
const double eps=1e-8;
const double pi=acos(-1.0);
template<class T,class T2>int chkmin(T &a,T2 b){return a>b?a=b,1:0;}
template<class T,class T2>int chkmax(T &a,T2 b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T,class T2>T mmin(T a,T2 b){return a<b?a:b;}
template<class T,class T2>T mmax(T a,T2 b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
template<class T>int dcmp(T a,T b){return a>b;}
template<int *a>int cmp_a(int x,int y){return a[x]<a[y];}
template<class T>bool sort2(T &a,T &b){return a>b?swap(a,b),1:0;}
#define min mmin
#define max mmax
#define abs aabs
struct __INIT__{
	__INIT__(){
		fill((unsigned char*)&inf,(unsigned char*)&inf+sizeof(inf),0x3f);
	}
}__INIT___;
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	template<typename A>
	inline bool read (A &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;else if(c==EOF)return 0;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
		return 1;
	}
	inline bool read (char &x) {
		while((x=gc())==' '||x=='\n' || x=='\r');
		return x!=EOF;
	}
	inline bool read(char *x){
		while((*x=gc())=='\n' || *x==' '||*x=='\r');
		if(*x==EOF)return 0;
		while(!(*x=='\n'||*x==' '||*x=='\r'||*x==EOF))*(++x)=gc();
		*x=0;
		return 1;
	}
	template<typename A,typename ...B>
	inline bool read(A &x,B &...y){
		return read(x)&&read(y...);
	}
	template<typename A>
	inline bool write (A x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
		return 0;
	}
	inline bool write (char x) {
		putc(x);
		return 0;
	}
	inline bool write(const char *x){
		while(*x){putc(*x);++x;}
		return 0;
	}
	inline bool write(char *x){
		while(*x){putc(*x);++x;}
		return 0;
	}
	template<typename A,typename ...B>
	inline bool write(A x,B ...y){
		return write(x)||write(y...);
	}
	//no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: read;
using io :: putc;
using io :: write;

__gnu_pbds::gp_hash_table<int,int> mus,fs;
bitset<1000005>np,gg;
int k;
vector<int> d,dm;
int mu[1000005];
int smu[1000005],sf[1000005];
int pri[100005],ps;
const int w=1000000;
void init(){
	mu[1]=1;
	smu[1]=1;
	np[1]=1;
	for(int i=2;i<=w;++i){
		if(!np[i]){
			pri[++ps]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=ps && i*pri[j]<=w;++j){
			np[i*pri[j]]=1;
			if(i%pri[j])mu[i*pri[j]]=-mu[i];
			else{mu[i*pri[j]]=0;break;}
		}
		smu[i]=smu[i-1]+mu[i];
	}
	for(int i=1;i<=k;++i)if(!(k%i)){
		d.push_back(i);
		if(mu[i])dm.push_back(i);
	}
	for(auto i:dm)if(!np[i]){
		for(int j=i;j<=w;j+=i)gg[j]=1;
	}
	for(int i=1;i<=w;++i){
		sf[i]=sf[i-1];
		if(!gg[i])sf[i]+=mu[i];
	}
}
int s_mu(int n){
	if(n<=w)return smu[n];
	if(mus.find(n)!=mus.end())return mus[n];
    ll ans=1;
    for(ll l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=ans-(r-l+1)*s_mu(n/r);
    }
	return mus[n]=ans;
}
int f(int);
int g(int n,int d){
	return mu[d]*f(n/d);
}
int f(int n){
	if(n<=w){return sf[n];}
	if(fs.find(n)!=fs.end())return fs[n];
	int ans=s_mu(n);
	for(auto i:dm)if(i>1)ans-=g(n,i);
	return fs[n]=ans;
}
int v1[2005],s[2005][2005];
int lcm(int a,int b){return a/__gcd(a,b)*b;}
int solve(int l,int r,int w){
	for(int i=0;i<dm.size();++i)v1[i]=g(r,dm[i])-g(l-1,dm[i]);
	int ans(0);
	for(int i=0;i<dm.size();++i)for(int j=0;j<d.size();++j)ans+=mu[d[j]]*v1[i]*(w/s[i][j]);
	return ans;
}
signed main(){
#ifdef QAQAutoMaton 
	freopen("cyclic.in","r",stdin);
	freopen("cyclic.out","w",stdout);
#endif
	int n,m;
	read(n,m,k);
	init();
	for(int i=0;i<dm.size();++i)for(int j=0;j<d.size();++j)s[i][j]=lcm(dm[i],d[j])/dm[i];
	int ans(0);
	for(int l=1,r;l<=n && l<=m;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(n/l)*solve(l,r,m/l);
	}
	write(ans,'\n');
	return 0;
}