码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ4652][NOI2016]循环之美

时间:2018-01-21 22:34:22      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:一个   前缀和   class   amp   http   const   bzoj   online   get   

BZOJ
Luogu

sol

这么久了终于把这个杜教筛的坑给填完了
首先有这样一个性质:要让一个分数在k进制下是纯循环小数,就必须满足其分母y与k互质。
所以这题我们求的就是\[\sum_{i=1}^{n}\sum_{j=1}^{m}[j\bot k][i\bot j]\]
\[\sum_{i=1}^{n}\sum_{j=1}^{m}[j\bot k][i\bot j]=\sum_{j=1}^{m}[j\bot k]\sum_{i=1}^{n}[i\bot j]\\=\sum_{j=1}^{m}[j\bot k]\sum_{i=1}^{n}\sum_{d|\gcd(i,j)}\mu(d)\\=\sum_{d=1}^{min(n,m)}\mu(d)\lfloor\frac nd\rfloor\sum_{j=1}^{m}[jd\bot k]\\=\sum_{d=1}^{min(n,m)}\mu(d)[d\bot k]\lfloor\frac nd\rfloor\sum_{j=1}^{m/d}[j\bot k]\]
所以我们现在要求函数\(F(n,k)=\sum_{i=1}^{n}[i\bot k]\)和函数\(S(n,k)=\sum_{i=1}^{n}\mu(i)[i\bot k]\)的前缀和。
前面一个好求,因为若\(i\bot k\),则有\(i+nk\bot k\),所以只要预处理出\(k\)以内的就可以了。
后面那个,需要推一推式子。
\[S(n,k)=\sum_{i=1}^{n}\mu(i)[i\bot k]\\=\sum_{i=1}^{n}\mu(i)\sum_{d|i,d|k}\mu(d)\\=\sum_{d|k}\mu(d)\sum_{d|i}\mu(i)\\=\sum_{d|k}\mu(d)\sum_{i=1}^{n/d}\mu(id)\\=\sum_{d|k}\mu(d)\sum_{i=1}^{n/d}\mu(i)\mu(d)[i\bot d]\\=\sum_{d|k}\mu(d)^2\sum_{i=1}^{n/d}\mu(i)[i\bot d]\\=\sum_{d|k}\mu(d)^2S(\lfloor\frac nd\rfloor,d)\]
然后会发现递归到\(k=1\)时就已经是边界了。
发现\(S(n,1)=\sum_{i=1}^{n}\mu(i)\),就是\(\mu(i)\)的前缀和呀。直接掏出杜教筛。

code

记得判质数的数组一定要开成bool不然就会英勇地MLE

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
#define RG register
const int N = 10000000;
inline int gi()
{
    RG int x=0,w=1;RG char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
bool zhi[N+5];
int pri[N],tot,mu[N+5],s[N+5],f[2005];
map<pair<int,int>,int>M;
inline void Sieve()
{
    zhi[1]=true;mu[1]=1;
    for (RG int i=2;i<=N;++i)
    {
        if (!zhi[i]) pri[++tot]=i,mu[i]=-1;
        for (RG int j=1;j<=tot&&i*pri[j]<=N;++j)
        {
            zhi[i*pri[j]]=true;
            if (i%pri[j]) mu[i*pri[j]]=-mu[i];
            else break;
        }
    }
    for (RG int i=1;i<=N;++i) s[i]=s[i-1]+mu[i];
}
int S(RG int n,RG int k)
{
    if (!n) return 0;
    if (k==1&&n<=N) return s[n];
    if (M[make_pair(n,k)]) return M[make_pair(n,k)];
    RG int res=0;
    if (k==1)
    {
        res=1;RG int i=2,j;
        while (i<=n)
        {
            j=n/(n/i);
            res-=(j-i+1)*S(n/i,1);
            i=j+1;
        }
    }
    else
    {
        for (RG int i=1;i*i<=k;++i)
            if (k%i==0)
            {
                if (mu[i]) res+=S(n/i,i);
                if (i*i!=k&&mu[k/i]) res+=S(n/(k/i),k/i);
            }
    }
    return M[make_pair(n,k)]=res;
}
int gcd(RG int a,RG int b){return b?gcd(b,a%b):a;}
int main()
{
    Sieve();
    RG int n=gi(),m=gi(),k=gi();
    for (RG int i=1;i<=k;++i) f[i]=f[i-1]+(gcd(i,k)==1);
    RG int i=1,j,gg,pre=0,cur;RG long long ans=0;
    while (i<=n&&i<=m)
    {
        j=min(n/(n/i),m/(m/i));
        gg=(m/i)/k*f[k]+f[(m/i)%k];
        cur=S(j,k);
        ans+=1ll*(n/i)*gg*(cur-pre);
        i=j+1;pre=cur;
    }
    printf("%lld\n",ans);
    return 0;
}

[BZOJ4652][NOI2016]循环之美

标签:一个   前缀和   class   amp   http   const   bzoj   online   get   

原文地址:https://www.cnblogs.com/zhoushuyu/p/8325727.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!