BZOJ4652:循环之美(数学+杜教筛)

题面
这题天秀
题意:求有多少个互质的x,y,x≤n,y≤m
使得 xy x y 的小数在k进制下从第一位开始无限循环

我看到这题,突然发现和我小学5年级奥数学的内容很像(我小学好厉害)

当时讲了怎么把一个循环小数化成分数
从第一位开始循环的小数
循环节长度就是分母中9的个数
分子就是循环节
(证明就是等比数列求和,然后求极限,好像当时也讲了)
比如

0.123456123456123456...=123456999999 0.123456123456123456... = 123456 999999

若不从第一位开始的就各种解方程的骚方法求出来
比如

0.123456666666...=690.54321 0.123456666666... = 6 9 − 0.54321

(小学奥数普及)

推导到k进制
分母应该只能是若干个(k-1)
约分后也是若干个(k-1)的约数
就必定与k互质了

正经的证明及以下的题解


f(n,m,k)=i=1nj=1m[(i,j)=1][(j,k)=1] f ( n , m , k ) = ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = 1 ] [ ( j , k ) = 1 ]

=i=1njd=1md|jd,d|kμ(d) = ∑ i = 1 n ∑ j d = 1 m ∑ d | j d , d | k μ ( d )

一轮画柿子
=d|kμ(d)f(md,n,d) = ∑ d | k μ ( d ) f ( m d , n , d )

k=1时为杜教筛
然后玄学45000ms卡过bzoj
洛谷T两个点

其实根据套路,玄学都要记忆化才能过
正解也是玄学
所以玄学的维数不要太多

有点T的玄学

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=6006000;
LL n,m,k;
int miu[N],smiu[N];
int prime[N],num;
bool b[N];

map<int,LL> mp;

int gm(int x)
{
    if(x<N)
    return smiu[x];
    if(mp.count(x))
    return mp[x];
    int res=1;
    for(int i=2,last;i<=x;i=last+1)
    {
        last=x/(x/i);
        res-=gm(x/i)*(last-i+1);
    }
    mp[x]=res;
    return res;
}

LL G(int x,int y)
{
    if(x>y)
    swap(x,y);
    LL res=0;
    for(int i=1,last;i<=x;i=last+1)
    {
        last=min(x/(x/i),y/(y/i));
        res+=(LL)(gm(last)-gm(i-1))*(LL)(x/i)*(y/i);
    }
    return res;
}

LL F(int x,int y,int d)
{
    if(!x||!y)
    return 0;
    if(d==1)
    return G(x,y);
    LL res=0;
    for(int i=1;i*i<=d;i++)
    if(d%i==0)
    {
        res+=(LL)miu[i]*F(y/i,x,i);
        if(i*i<d)
        res+=(LL)miu[d/i]*F(y/(d/i),x,d/i);
    }
    return res;
}

int main()
{
    miu[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!b[i])
        prime[++num]=i,miu[i]=-1;
        for(int j=1;j<=num&&i*prime[j]<N;j++)
        {
            miu[i*prime[j]]=-miu[i];
            b[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                miu[i*prime[j]]=0;
                break;
            }
        }
    }
    for(int i=1;i<N;i++)
    smiu[i]=smiu[i-1]+miu[i];

    cin>>n>>m>>k;
    cout<<F(n,m,k)<<endl;

    return 0;
}

传说中的正解

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=6006000;
LL n,m,k,ans;
int u[N],miu[N],smiu[N],phi[N],f[N];
int prime[N],num;
bool b[N];

map<int,LL> mp;
map<LL,LL> mmp;

int gm(int x)
{
    if(x<N)
    return smiu[x];
    if(mp.count(x))
    return mp[x];
    int res=1;
    for(int i=2,last;i<=x;i=last+1)
    {
        last=x/(x/i);
        res-=gm(x/i)*(last-i+1);
    }
    mp[x]=res;
    return res;
}

int gcd(int x,int y)
{
    if(!y)
    return x;
    return gcd(y,x%y);
}

int lj(int x)
{
    return (x/k)*phi[k]+f[x%k];
}

LL F(int x,int y)
{
    if(mmp.count(2001ll*x+y))
    return mmp[2001ll*x+y];
    if(!x)
    return 0;
    if(y==1)
    return gm(x);
    LL res=F(x,y/u[y])+F(x/u[y],y);
    mmp[2001ll*x+y]=res;
    return res;
}

int main()
{
    phi[1]=miu[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!b[i])
        prime[++num]=i,phi[i]=i-1,miu[i]=-1,u[i]=i;
        for(int j=1;j<=num&&i*prime[j]<N;j++)
        {
            miu[i*prime[j]]=-miu[i];
            b[i*prime[j]]=1;
            u[i*prime[j]]=prime[j];
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                miu[i*prime[j]]=0;
                break;
            }
        }
    }

    for(int i=1;i<N;i++)
    smiu[i]=smiu[i-1]+miu[i];

    cin>>n>>m>>k;
    int tu=1;
    while(k>1)
    {
        tu*=u[k];
        while(u[k/u[k]]==u[k])
        k/=u[k];
        k/=u[k];
    }
    k=tu;

    for(int i=1;i<=k;i++)
    if(gcd(i,k)==1)
    f[i]++;
    for(int i=1;i<=k;i++)
    f[i]+=f[i-1];

    for(int i=1,last;i<=min(n,m);i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans+=(F(last,k)-F(i-1,k))*(LL)(n/i)*lj(m/i);
    }

    cout<<ans<<endl;

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值