BZOJ4652:循環之美(數學+杜教篩)

題面
這題天秀
題意:求有多少個互質的x,y,x≤n,y≤m
使得xy 的小數在k進制下從第一位開始無限循環

我看到這題,突然發現和我小學5年級奧數學的內容很像(我小學好厲害)

當時講了怎麼把一個循環小數化成分數
從第一位開始循環的小數
循環節長度就是分母中9的個數
分子就是循環節
(證明就是等比數列求和,然後求極限,好像當時也講了)
比如

0.123456123456123456...=123456999999

若不從第一位開始的就各種解方程的騷方法求出來
比如

0.123456666666...=690.54321

(小學奧數普及)

推導到k進制
分母應該只能是若干個(k-1)
約分後也是若干個(k-1)的約數
就必定與k互質了

正經的證明及以下的題解


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

=i=1njd=1md|jd,d|kμ(d)

一輪畫柿子
=d|kμ(d)f(md,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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章