「UOJ221 NOI2016 循环之美」 - 数论数学

本文解析了UOJ221(NOI2016循环之美)题目,通过数学变换求解特定条件下纯循环小数的数量。采用数论方法,通过杜教筛算法优化计算。

UOJ221 NOI2016 循环之美

tags:数论,数学

题意

让你找在 \(K\) 进制下有多少个数值互不相等的纯循环小数

题解

首先题意转化为

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

然后经过一波化简之后

\[ Ans=\sum_{d=1}^{\min(n,m)}\lfloor\frac nd\rfloor\mu(d)[\gcd(d,k)=1]\sum_{j=1}^{\lfloor\frac md\rfloor}[\gcd(j,k)=1] \]

然后整除分块,后半个式子随便算,前半个式子

\[ \begin{aligned} &\sum_{d=1}^n\mu(d)[\gcd(d,k)=1]\\ =&\sum_{d=1}^n\mu(d)-\sum_{d=1}\mu(d)[\gcd(d,k)>1]\\ =&\sum_{d=1}^n\mu(d)-\sum_{e>1,e|k}\sum_{d=1}^{\lfloor\frac ne\rfloor}\mu(de)[\gcd(de,k)=d]\\ =&\sum_{d=1}^n\mu(d)-\sum_{e>1,e|k}\sum_{d=1}^{\lfloor\frac ne\rfloor}\mu(d)\mu(e)[\gcd(d,e)=1][\gcd(de,k)=e]\\ =&\sum_{d=1}^n\mu(d)-\sum_{e>1,e|k}\mu(e)\sum_{d=1}^{\lfloor\frac ne\rfloor}\mu(d)[\gcd(d,k)=1] \end{aligned} \]

然后大力杜教筛就好了

后半个式子根本推不出来,我果然好菜

#include<map>
#include<cstdio>
#include<vector>
#include<unordered_map>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
template<typename T>inline void rd(T&x){int fl=0,ch;while(ch=getchar(),ch<48||57<ch)fl^=!(ch^45);x=(ch&15);while(ch=getchar(),47<ch&&ch<58)x=x*10+(ch&15);if(fl)x=-x;}
template<typename T>inline void pt(T x){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);}
template<typename T>inline void pt(T x,int ch){pt(x),putchar(ch);}
template<typename T>inline T max(const T&x,const T&y){return x<y?y:x;}
template<typename T>inline T min(const T&x,const T&y){return x<y?x:y;}
int gcd(int a,int b){while(a^=b^=a^=b%=a,a);return b;} 
const int N=1000005;
int n,m,k,vis[N],p[N],mu[N],s[N],s1[N];std::vector<int>v;std::unordered_map<int,int>mmp,mmp1;
void pre(){
    vis[0]=vis[1]=1,mu[1]=1;
    for(int i=2;i<N;++i){
        if(!vis[i])p[++*p]=i,mu[i]=-1;
        for(int j=1;j<=*p&&i*p[j]<N;++j){
            vis[i*p[j]]=1;
            if(i%p[j])mu[i*p[j]]=-mu[i];
            else break;
        }
    }
    for(int i=1;i<N;++i)s[i]=s[i-1]+mu[i];
    for(int i=1;i<N;++i)s1[i]=s1[i-1]+mu[i]*(gcd(i,k)==1);
}
int F0(int n){
    if(n<N)return s[n];
    if(mmp.count(n))return mmp[n];
    int res=1;
    for(int l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        res-=F0(n/l)*(r-l+1);
    }
    return mmp[n]=res;
}
int F1(int n){
    if(n<N)return s1[n];
    if(mmp1.count(n))return mmp1[n];
    int res=F0(n);
    for(int i=1;i<(int)v.size();++i){
        int e=v[i];
        res-=mu[e]*F1(n/e);
    }
    return mmp1[n]=res;
}
int F2(int n){
    int res=0;
    for(int i=0;i<(int)v.size();++i){
        int d=v[i];
        res+=mu[d]*(n/d);
    }
    return res;
}
signed main(){
    rd(n),rd(m),rd(k);pre();
    for(int i=1;i<=k;++i)if(mu[i]&&k%i==0)v.push_back(i);
    long long ans=0;
    for(int l=1,r;l<=n&&l<=m;l=r+1){
        r=min(m/(m/l),n/(n/l));
        ans+=(long long)n/l*(F1(r)-F1(l-1))*F2(m/l);
    }
    pt(ans,'\n');
    return 0;
}

转载于:https://www.cnblogs.com/xay5421/p/UOJ211.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值