[NOI2016]循环之美

[NOI2016]循环之美


对于已知的十进制数\(n\)\(m\),在\(k\)进制下,有多少个数值上互不相等的纯循环小数,可以用分数\(\frac xy\)表示,其中\(1\leq x\leq n,1\leq y\leq m\),且\(x,y\in\mathbb{Z}\)

首先\(k\)进制下如果\((y,k)=1\),那么\(\frac xy\)是纯循环的。当然还要满足\((x,y)=1\)证明略

那么答案是:\(f(n,m,k)=\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1][(j,k)=1]\)

我也不知道是怎么想到推过去的。。。可能考场上只能dfs推公式了。。

\(\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]\sum_{d|j,d|k}\mu(d)\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{d|j}^m[(i,j)=1]\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\frac md}[(i,jd)=1]\)
\(\sum_{d|k}\mu(d)\sum_{i=1}^n\sum_{j=1}^{\frac md}[(i,j)=1][(i,d)=1]\)
\(\sum_{d|k}\mu(d)f(\frac md,n,k)\)

可以暴力枚举约数,用map记忆化

一个很有用的优化,如果\(\mu(d)=0\)则不用递归下去

边界条件就是\(f(n,m,1)\)

\(f(n,m,1)=\sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]=\sum_{d=1}^n\mu(d)\lfloor\frac nd\rfloor\lfloor\frac md\rfloor(n\leq m)\)

杜教筛即可

#include<bits/stdc++.h>
#define il inline
#define vd void
#define ull unsigned long long
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))f^=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?x:-x;
}
#define maxn 2000000
int pr[maxn],p,mu[maxn];
bool yes[maxn];
std::unordered_map<int,int>_smu;
il int smu(int n){
    if(n<maxn)return mu[n];
    if(_smu[n])return _smu[n];
    int ret=1,qt=sqrt(n);
    for(int i=2;i<=qt;++i)ret-=smu(n/i);
    for(int i=qt+1,j;i<=n;i=j+1)j=n/(n/i),ret-=smu(n/i)*(j-i+1);
    return _smu[n]=ret;
}
std::vector<int>d[2010];
int muqwq[2010];
struct data{int n,m,k;};
std::map<data,ll>_solve;
il bool operator<(const data&a,const data&b){
    if(a.n!=b.n)return a.n<b.n;
    if(a.m!=b.m)return a.m<b.m;
    return a.k<b.k;
}
il ll solve(int n,int m,int k){
    if(!n||!m)return 0;
    data qwq={n,m,k};
    if(_solve.find(qwq)!=_solve.end())return _solve[qwq];
    ll ret=0;
    if(k==1){
        if(n>m)std::swap(n,m);
        int lst=0,smuj;
        for(int i=1,j;i<=n;i=j+1){
            j=std::min(n/(n/i),m/(m/i));
            smuj=smu(j);
            ret+=1ll*(smuj-lst)*(n/i)*(m/i);
            lst=smuj;
        }
        return ret;
    }else for(auto i:d[k])ret+=solve(m/i,n,i)*muqwq[i];
    return _solve[qwq]=ret;
}
ll F[2010];
int main(){
    int n=gi(),m=gi(),k=gi();
    mu[1]=1;
    for(int i=2;i<maxn;++i){
        if(!yes[i])pr[++p]=i,mu[i]=-1;
        for(int j=1;j<=p&&i*pr[j]<maxn;++j){
            yes[i*pr[j]]=1;
            if(i%pr[j])mu[i*pr[j]]=-mu[i];
            else{mu[i*pr[j]]=0;break;}
        }
    }
    for(int i=1;i<=k;++i)muqwq[i]=mu[i];
    for(int i=1;i<maxn;++i)mu[i]+=mu[i-1];
    for(int i=1;i<=k;++i)if(k%i==0&&muqwq[i])for(int j=i;j<=k;j+=i)d[j].emplace_back(i);
    printf("%lld\n",solve(n,m,k));
    #ifdef DEBUG
    fprintf(stderr,"%.6lf\n",1.0*clock()/CLOCKS_PER_SEC);
    #endif
    return 0;
}
posted @ 2019-06-18 22:06  菜狗xzz  阅读(224)  评论(0编辑  收藏  举报