标签(空格分隔): 数论
题目链接1
题目链接2
题目链接3
一个推论:
一个最简分数\(\frac{x}{y}\)在k进制下是纯循环的,当且仅当\(\gcd(y,k)=1\)。
证明:
不妨设\(x\leq y\)。最简分数即\(\gcd(x,y)=1\)。纯循环即小数位的第1位属于循环节,那么就是存在p满足\(x\equiv x\times k^p \pmod y\)即余数出现循环是从小数点对应第一位的余数开始的。
由于\((x,y)=1\),所以当且仅当存在p使\(1 \equiv k^p \pmod y\),那么\(\frac{x}{y}\)就是纯循环的。
而这个结论成立当且仅当\((k,y)=1\),所以得证。
有了这个推论,我们要求的式子一目了然:
然后就是数学式子的推导了。我尽可能写的在清晰的情况下易懂。
令\(f(n)=\sum_{i=1}^{n}[(i,k)=1]\)。
根据\(\gcd(a,b)=\gcd(a+b,b)=\gcd(a+kb,b)\),我们可以知道\(f(n)=\left\lfloor\frac{n}{k}\right\rfloor\cdot f(k) + f(n\bmod k)\),所以\(f\)函数预处理\(O(k\log k)\),查询\(O(1)\)。
令\(S(n)=\sum_{i=1}^{n}[(i,k)=1]\cdot \mu(i)\),考虑通过类似洲阁筛的DP求出\(S\)。
定义\(s(i,n)\)表示小于等于\(n\)且与\(k\)的前\(i\)种质因子互质的数\(d\)的\(\mu(d)\)之和,初始化\(s(i,n)=\sum_{d=1}^{n}\mu(d)\),用杜教筛即可。
还是比较好理解的。意思就是从与前\(i-1\)互质的所有\(\mu\)中减去不与\(i\)互质的。由于\(\mu\)不是完全积性函数,所以要求除去\(p_i\)后与前i互质而不是与前\(i-1\)互质,因此要注意第二维转移应是递增转移。
根据\((1)\)式可得,\(s(i,n)\)函数的第二维只有\(O(\sqrt{n}+\sqrt{m})\)种取值,所以DP的复杂度是预处理\(O(n^{\frac{2}{3}})\)+DP\(O(w(k)\cdot (\sqrt{n}+\sqrt{m}))\)(\(w(k)\)表示k的不同质因子个数,由于\(k\leq 2000\),\(w(k)\)最大也就是\(4\)),也是本题最终复杂度。
code
#include<stdio.h>
#include<set>
#include<map>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long LL;
int n,m,K,maxn;
int mu[1000010];
bool flag[1000010];
int prime[500010],tot=0;
void Init(){
mu[1]=1;
for(int i=2;i<=maxn;++i){
if(!flag[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*prime[j]<=maxn;++j){
flag[i*prime[j]]=true;
if(i%prime[j]==0) break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=maxn;++i) mu[i]+=mu[i-1];
}
map<int,int> M;
int Gcd(int a,int b){return b==0?a:Gcd(b,a%b);}
int Mu(int x){
if(x<=maxn) return mu[x];
if(M.count(x)) return M[x];
int ans=0;
for(int i=2,j;i<=x;i=j+1){
j=x/(x/i);
ans+=(j-i+1)*Mu(x/i);
}
M[x]=1-ans;
return 1-ans;
}
#define R(x) (x<=sqr?x:cnt-(n/x)+1)
int H[120010],cnt;
map<int,int> R;
set<int> P;
int p[10],tmp,F[2010];
inline int f(int x){return (x/K)*F[K]+F[x%K];}
int S[120010];
int main(){
freopen("cyclic.in","r",stdin);
freopen("cyclic.out","w",stdout);
scanf("%d%d%d",&n,&m,&K);
maxn=(int)pow(n,2.0/3.0);
Init();
for(int i=1;i*i<=n;++i) P.insert(i),P.insert(n/i);
for(int i=1;i*i<=m;++i){
if(i<=n) P.insert(i);
if(m/i<=n) P.insert(m/i);
}
for(set<int>::iterator iter=P.begin();iter!=P.end();++iter){
R[*iter]=++cnt;
H[cnt]=*iter;
}
for(int i=1;i<=cnt;++i) S[i]=Mu(H[i]);
for(int i=1;i<=K;++i) F[i]=F[i-1]+(Gcd(i,K)==1);
int x=K;
for(int i=2;i*i<=x;++i){
if(x%i==0){
p[++tmp]=i;
while(x%i==0) x/=i;
}
}
if(x>1) p[++tmp]=x;
for(int i=1;i<=tmp;++i){
for(int j=1;j<=cnt;++j){
if(H[j]<p[i]) continue;
S[j]+=S[R[H[j]/p[i]]];
}
}
LL Last=0,Now,ans=0;
for(int i=1,j,y=min(n,m);i<=y;i=j+1){
j=min(n/(n/i),m/(m/i));
Now=S[R[j]];
ans+=(LL)(Now-Last)*(n/i)*f(m/i);
Last=Now;
}
printf("%lld\n",ans);
getchar(); getchar();
fclose(stdin); fclose(stdout);
return 0;
}