【BZOJ】4652: [Noi2016]循环之美-数论

博客介绍了NOI2016中关于循环之美的一道数论问题。文章指出,当分母n与进制k互质(gcd(n, k)=1)时,满足题目要求的小数具有一特定性质。通过数学公式简化,问题转化为求解一系列最简分数,并利用杜教筛和递归方法进行计算。代码部分展示了实现细节。" 113999342,10544562,SILK编解码:开发与解码实战,"['silk', '编解码', '音频处理', 'Android开发', 'C++']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

数学公式什么的太难打了。
我们可以发现一个规律,设分母为n,进制为k,当nkn⊥kgcd(n,k)=1gcd(n,k)=1时,该小数性质满足题目要求(并不会证)
可以得到:ni=1mj=1[ij][jk]∑i=1n∑j=1m[i⊥j][j⊥k]
iji⊥j 是保证最简分数,jkj⊥k已说明。
经过一番化简:nd=1[dk]μ(d)ndmdj=1[jk]∑d=1n[d⊥k]μ(d)⌊nd⌋∑j=1⌊md⌋[j⊥k]
前面一坨(nd=1[dk]μ(d)∑d=1n[d⊥k]μ(d))可以递归+杜教筛求得,后面一坨(mdj=1[jk]∑j=1⌊md⌋[j⊥k])由于k只有2000,可以之间算出来。

代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<cstring>
#define mkp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
const int N=5e6+5,lim=5e6;
int n,m,k,g[2002],mu[N],sum[N],p[N],num,mn;
bool pri[N];
ll ans,cur,past;
map<pair<int,int>,int>mp;
inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}

inline void pre()
{
    int i,j,now;
    for(i=1;i<=k;++i) g[i]= gcd(i,k)==1? g[i-1]+1:g[i-1];
    memset(pri,true,sizeof(pri));mu[1]=1;
    for(i=2;i<=lim;++i){
        if(pri[i]){mu[i]=-1;p[++num]=i;}
        for(j=1;j<=num && 1ll*p[j]*i<=(ll)lim;++j){
            pri[(now=p[j]*i)]=false;
            if(i%p[j]==0){mu[now]=0;break;}
            mu[now]=-mu[i];
        }
    }
    for(i=1;i<=lim;++i) sum[i]=sum[i-1]+mu[i]; 
}
inline int G(int x){return (x/k)*g[k]+g[x%k];}

inline int F(int x,int k)
{    
   if((k==1 && x<=lim)||(!x)) return sum[x];
   if(mp[mkp(x,k)]) return mp[mkp(x,k)];
   if(k==1){
       int ret=1;
       for(int i=2,j;i<=x;i=j+1){
         j=x/(x/i);
         ret-=(j-i+1)*F(x/i,1);
       }
       mp[mkp(x,k)]=ret;
       return ret;
   }else{
       int ret=0;
       for(int i=1;i*i<=k;++i){
          if(k%i==0){
            if(mu[i]) ret+=F(x/i,i);
            if(i*i!=k && mu[k/i]) ret+=F(x/(k/i),k/i);
          }
       }
       mp[mkp(x,k)]=ret;
       return ret;
   }
}

int main(){
   int i,j;
   scanf("%d%d%d",&n,&m,&k);
   pre();mn=min(n,m);
   for(i=1;i<=mn;i=j+1){
     j=min(n/(n/i),m/(m/i));
     cur=F(j,k);
     ans+=1ll*n/i*(cur-past)*G(m/i);
     past=cur;
   }
   printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值