题意:求∑nx=1∑my=1xy∑x=1n∑y=1mxy在kk进制下能表示成循环节从第一位小数开始的无限循环小数或整数的最简分数个数
假设的循环节长度为ll
这里中括号表示其小数部分,意思就是把小数点往后挪ll位,小数部分还是相同
比如小数点往后挪3位变成1123.123…1123.123…
参照10进制下小数点往后挪ll位就是乘以
那么kk进制下小数点往后挪位就是乘以klkl
⇒xkly−⌊xkly⌋=xy−⌊xy⌋⇒xkly−⌊xkly⌋=xy−⌊xy⌋
⇒xkl−⌊xkly⌋∗y=x−⌊xy⌋∗y⇒xkl−⌊xkly⌋∗y=x−⌊xy⌋∗y
⇒xkl≡xmody⇒xkl≡xmody
题目要求最简分数,所以(x,y)=1(x,y)=1
⇒kl≡1mody⇒kl≡1mody
⇒(k,y)=1⇒(k,y)=1
⇒Ans=∑ni=1∑mj=1[(i,j)=1][(j,k)=1]⇒Ans=∑i=1n∑j=1m[(i,j)=1][(j,k)=1]
那么接下来就有两条路可以走了
一:考虑处理[(i,j)=1][(i,j)=1]
(有公式恐惧症下转法二)
∑ni=1∑mj=1[(i,j)=1][(j,k)=1]∑i=1n∑j=1m[(i,j)=1][(j,k)=1]
=∑mj=1[(j,k)=1]∑ni=1[(i,j)=1]=∑j=1m[(j,k)=1]∑i=1n[(i,j)=1]
=∑mj=1[(j,k)=1]∑ni=1∑d|(i,j)μ(d)=∑j=1m[(j,k)=1]∑i=1n∑d|(i,j)μ(d)
=∑mj=1[(j,k)=1]∑ni=1∑d|i,d|jμ(d)=∑j=1m[(j,k)=1]∑i=1n∑d|i,d|jμ(d)
=∑mj=1[(j,k)=1]∑nd|jμ(d)nd=∑j=1m[(j,k)=1]∑d|jnμ(d)nd
=∑nd=1μ(d)nd∑mj=1[d|j][(j,k)=1]=∑d=1nμ(d)nd∑j=1m[d|j][(j,k)=1]
=∑nd=1μ(d)nd∑mjd=1[d|jd][(jd,k)=1]=∑d=1nμ(d)nd∑jd=1m[d|jd][(jd,k)=1]
jj用替换一下来减少限制(下同)
=∑nd=1μ(d)nd∑mdj=1[(jd,k)=1]=∑d=1nμ(d)nd∑j=1md[(jd,k)=1]
=∑nd=1μ(d)[(d,k)=1]nd∑mdj=1[(j,k)=1]=∑d=1nμ(d)[(d,k)=1]nd∑j=1md[(j,k)=1]
考虑f(n)=∑ni=1[(i,k)=1]=nkϕ(k)+f(nmodk)f(n)=∑i=1n[(i,k)=1]=nkϕ(k)+f(nmodk)
暴力O(klogk)gcdO(klogk)gcd算一下f(1)…f(k)f(1)…f(k)
接下来貌似又有两条路可以走了
①:考虑化简式子
考虑g(n,k)=∑nd=1μ(d)[(d,k)=1]g(n,k)=∑d=1nμ(d)[(d,k)=1]
=∑nd=1μ(d)∑p|(d,k)μ(p)=∑d=1nμ(d)∑p|(d,k)μ(p)
=∑nd=1μ(d)∑p|d,p|kμ(p)=∑d=1nμ(d)∑p|d,p|kμ(p)
=∑nd=1∑p|dμ(d)∑p|kμ(p)=∑d=1n∑p|dμ(d)∑p|kμ(p)
=∑p|kμ(p)∑ndp=1∑p|dpμ(dp)=∑p|kμ(p)∑dp=1n∑p|dpμ(dp)
=∑p|kμ(p)∑npd=1μ(dp)=∑p|kμ(p)∑d=1npμ(dp)
如果(d,p)≠1(d,p)≠1,那么μ(dp)=0μ(dp)=0
=∑p|kμ(p)∑npd=1μ(dp)[(d,p)=1]=∑p|kμ(p)∑d=1npμ(dp)[(d,p)=1]
=∑p|kμ(p)∑npd=1μ(d)μ(p)[(d,p)=1]=∑p|kμ(p)∑d=1npμ(d)μ(p)[(d,p)=1]
=∑p|kμ(p)2∑npd=1μ(d)[(d,p)=1]=∑p|kμ(p)2∑d=1npμ(d)[(d,p)=1]
=∑p|kμ(p)2g(np,p)=∑p|kμ(p)2g(np,p)
边界g(0,k)=0g(0,k)=0
g(n,1)=∑nd=1μ(d)g(n,1)=∑d=1nμ(d)直接杜教筛
复杂度是OO((的质因子个数)n−−√+n23n+n23)
#include<map>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define fp(i,a,b) for(int i=a,I=b;i<=I;++i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=7e5+5,E=2e6+5;
typedef int arr[N];typedef long long ll;
struct Am{int nx,x,w;}e1[E];
struct Ans{int nx,n,m,k;ll w;}e2[E];
int n,m,k,M,c1,c2,K[2000],f1[E],f2[E];arr is,pr,mu,Smu;
int Sm(int x){
if(x<=M)return Smu[x];
int u=(x+2017)%E;
for(int i=f1[u];i;i=e1[i].nx)if(e1[i].x==x)return e1[i].w;
e1[++c1]=Am{f1[u],x,1};f1[u]=c1;
int&w=e1[c1].w,i=2,j=sqrt(x);
for(;i<=j;++i)w-=Sm(x/i);
for(;i<=x;i=j+1)j=x/(x/i),w-=(j-i+1)*Sm(x/i);
return w;
}
ll sol(int n,int m,int k){
if(!n||!m)return 0;
int u=(2017ll*n+m+k)%E;
for(int i=f2[u];i;i=e2[i].nx)if(e2[i].n==n&&e2[i].m==m&&e2[i].k==k)return e2[i].w;
e2[++c2]=Ans{f2[u],n,m,k,0};f2[u]=c2;ll&w=e2[c2].w;
if(k==1){
if(n>m)swap(n,m);
int i=1,j=sqrt(n),s,t=0,x,y;
for(;i<=j;++i,t=s)s=Sm(i),w+=1ll*(n/i)*(m/i)*(s-t);
for(;i<=n;i=j+1,t=s)x=n/i,y=m/i,j=min(n/x,m/y),s=Sm(j),w+=1ll*x*y*(s-t);
u=(2017ll*m+n+k)%E;e2[++c2]=Ans{f2[u],m,n,k,w};f2[u]=c2;
}else for(int i=1;i<=K[0]&&K[i]<=k;++i)
if(k%K[i]==0&&mu[K[i]])
w+=sol(m/K[i],n,K[i])*mu[K[i]];
return w;
}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%d%d%d",&n,&m,&k);
M=min(N-5,max(k,min(n,m)));Smu[1]=mu[1]=1;
fp(i,2,M){
if(!is[i])pr[++pr[0]]=i,mu[i]=-1;
for(int j=1,x;j<=pr[0]&&(x=i*pr[j])<=M;++j){
is[x]=1;if(i%pr[j])mu[x]=-mu[i];else break;
}Smu[i]=Smu[i-1]+mu[i];
}
fp(i,1,k)if(k%i==0)K[++K[0]]=i;
printf("%lld",sol(n,m,k));
return 0;
}
②:考虑分析式子
我们可以把kk表示成,假设pp是最小的质因子
对于1到kk中每一个数可以预处理出他的
g(n,k)=∑nd=1μ(d)[(d,k)=1]=∑nd=1μ(d)[(d,p)=1][(d,q)=1]g(n,k)=∑d=1nμ(d)[(d,k)=1]=∑d=1nμ(d)[(d,p)=1][(d,q)=1]
当(d,q)=1(d,q)=1时只有(d,p)=1,(d,p)=p(d,p)=1,(d,p)=p两种情况
所以我们可以转化为求满足(d,q)=1(d,q)=1的μ(d)μ(d)减去(d,q)=1(d,q)=1中满足(d,p)=p(d,p)=p的μ(d)μ(d)
即g(n,k)=∑nd=1μ(d)[(d,q)=1]−∑nd=1μ(d)[(d,q)=1][(d,p)=p]g(n,k)=∑d=1nμ(d)[(d,q)=1]−∑d=1nμ(d)[(d,q)=1][(d,p)=p]
=g(n,q)−∑ndp=1μ(dp)[(dp,q)=1][(dp,p)=p]=g(n,q)−∑dp=1nμ(dp)[(dp,q)=1][(dp,p)=p]
=g(n,q)−∑npd=1μ(dp)[(d,q)=1]=g(n,q)−∑d=1npμ(dp)[(d,q)=1]
当(d,p)≠1(d,p)≠1时μ(dp)=0μ(dp)=0,所以dd要满足
=g(n,q)−∑npd=1μ(dp)[(d,p)=1][(d,q)=1]=g(n,q)−∑d=1npμ(dp)[(d,p)=1][(d,q)=1]
=g(n,q)−∑npd=1μ(d)μ(p)[(d,k)=1]=g(n,q)−∑d=1npμ(d)μ(p)[(d,k)=1]
=g(n,q)−μ(p)∑npd=1μ(d)[(d,k)=1]=g(n,q)−μ(p)∑d=1npμ(d)[(d,k)=1]
=g(n,q)+g(np,k)=g(n,q)+g(np,k)
边界和复杂度是同上
#include<cmath>
#include<cstdio>
#include<algorithm>
#define fp(i,a,b) for(int i=a,I=b;i<=I;++i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=1e6+5,E=2.2*N;
typedef int arr[N];
struct eg{int nx,n,k,w;}e[E];
int n,m,k,M,ce,fi[E],p[2001],q[2001];arr is,pr,mu,Smu,f;
int g(int n,int k){
if(!n||(n<=M&&k==1))return Smu[n];
int x=(1LL*n*2017+k)%E;
for(int i=fi[x];i;i=e[i].nx)if(e[i].n==n&&e[i].k==k)return e[i].w;
e[++ce]=eg{fi[x],n,k,0};fi[x]=ce;int&w=e[ce].w;
if(k==1){
w=1;int i=2,j=sqrt(n),x;
for(;i<=j;++i)w-=g(n/i,1);
for(;i<=n;i=j+1)x=n/i,j=n/x,w-=(j-i+1)*g(x,1);
}else w=g(n,q[k])+g(n/p[k],k);
return w;
}
int gcd(int a,int b){return!b?a:gcd(b,a%b);}
inline int Sf(int x){return x/k*f[k]+f[x%k];}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%d%d%d",&n,&m,&k);
M=min(N-5,min(n,m));Smu[1]=mu[1]=1;
fp(i,2,M){
if(!is[i])pr[++pr[0]]=i,mu[i]=-1;
for(int j=1,x;j<=pr[0]&&(x=i*pr[j])<=M;++j){
is[x]=1;if(i%pr[j])mu[x]=-mu[i];else break;
}Smu[i]=Smu[i-1]+mu[i];
}
fp(i,1,k)f[i]=f[i-1]+(gcd(i,k)==1);
fp(i,2,k){
for(int j=2;;++j)if(i%j==0&&!is[j]){p[i]=j;break;}
for(q[i]=i;q[i]%p[i]==0;)q[i]/=p[i];
}
int i=1,j=sqrt(min(n,m)),x,y,s,t=0;long long w=0;
for(;i<=j;++i,t=s)s=g(i,k),w+=1ll*(n/i)*Sf(m/i)*(s-t);
for(;i<=min(n,m);i=j+1,t=s)x=n/i,y=m/i,j=min(n/x,m/y),
s=g(j,k),w+=1ll*x*Sf(y)*(s-t);
printf("%lld",w);
return 0;
}
二:考虑处理[(j,k)=1][(j,k)=1]
令f(n,m,k)=∑ni=1∑mj=1[(i,j)=1][(j,k)=1]f(n,m,k)=∑i=1n∑j=1m[(i,j)=1][(j,k)=1]
=∑ni=1∑mj=1[(i,j)=1]∑d|(j,k)μ(d)=∑i=1n∑j=1m[(i,j)=1]∑d|(j,k)μ(d)
=∑ni=1∑mjd=1[(i,jd)=1]∑d|jd,d|kμ(d)=∑i=1n∑jd=1m[(i,jd)=1]∑d|jd,d|kμ(d)
=∑d|kμ(d)∑ni=1∑mdj=1[(i,jd)=1]=∑d|kμ(d)∑i=1n∑j=1md[(i,jd)=1]
=∑d|kμ(d)∑ni=1∑mdj=1[(i,j)=1][(i,d)=1]=∑d|kμ(d)∑i=1n∑j=1md[(i,j)=1][(i,d)=1]
=∑d|kμ(d)f(md,n,d)=∑d|kμ(d)f(md,n,d)
边界f(0,m,k)=f(n,0,k)=0f(0,m,k)=f(n,0,k)=0
然后k=1k=1就是简单处理一下∑ni=1∑mj=1[(i,j)=1]=∑nd=1μ(d)ndmd∑i=1n∑j=1m[(i,j)=1]=∑d=1nμ(d)ndmd就okok了
复杂度是O(lognlogmn−−√+n23)O(lognlogmn+n23)
#include<map>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define fp(i,a,b) for(int i=a,I=b;i<=I;++i)
#define fd(i,a,b) for(int i=a,I=b;i>=I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=1e7+5;
typedef int arr[N];
typedef long long ll;
struct da{int n,m,k;inline bool operator<(const da&b)const{return n==b.n?(m==b.m?k<b.k:m<b.m):n<b.n;}};
int n,m,k,M,K[2000];arr is,pr,mu,Smu;map<int,int>Am;map<da,ll>Ans;
int Sm(int x){
if(x<=M)return Smu[x];
if(Am[x])return Am[x];
int w=1,i=2,j=sqrt(x);
for(;i<=j;++i)w-=Sm(x/i);
for(;i<=x;i=j+1)j=x/(x/i),w-=(j-i+1)*Sm(x/i);
return Am[x]=w;
}
ll sol(int n,int m,int k){
if(!n||!m)return 0;
da now=da{n,m,k};
if(Ans[now])return Ans[now];ll w=0;
if(k==1){
if(n>m)swap(n,m);
int i=1,j=sqrt(n),s,t=0,x,y;
for(;i<=j;++i,t=s)s=Sm(i),w+=1ll*(n/i)*(m/i)*(s-t);
for(;i<=n;i=j+1,t=s)x=n/i,y=m/i,j=min(n/x,m/y),s=Sm(j),w+=1ll*x*y*(s-t);
Ans[da{m,n,k}]=w;
}else for(int i=1;i<=K[0]&&K[i]<=k;++i)
if(k%K[i]==0&&mu[K[i]])
w+=sol(m/K[i],n,K[i])*mu[K[i]];
return Ans[now]=w;
}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%d%d%d",&n,&m,&k);
M=min((int)1e7,max(k,min(n,m)));mu[1]=1;
fp(i,2,M){
if(!is[i])pr[++pr[0]]=i,mu[i]=-1;
for(int j=1,x;j<=pr[0]&&(x=i*pr[j])<=M;++j){
is[x]=1;
if(i%pr[j])mu[x]=-mu[i];
else{mu[x]=0;break;}
}
}fp(i,1,M)Smu[i]=Smu[i-1]+mu[i];
fp(i,1,k)if(k%i==0)K[++K[0]]=i;
printf("%lld",sol(n,m,k));
return 0;
}
如果是考场我还是会选择法二的