【BZOJ2440】【中山市选2011】完全平方数 二分+容斥+莫比乌斯函数线性筛

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44646027");
}

题解:

给出PoPoQQQ大爷的题解链接:
http://www.voidcn.com/article/p-kynkahpv-zz.html
我太弱,可以直接看大爷的不用看我的。

首先整体思想上我们可以二分check前x个数中有多少个符合要求的数。
然后这个怎么check呢?发现我们枚举每个数,看范围内是它的平方的倍数的数有多少个就行了。然后发现容斥一下,有些数是要加的,而有些数是要减的,比如枚举到3,那么3*3*4=36被删了一次,而枚举到2时其实2*2*9=36就已经被删了一次啦。所以在枚举2*3=6时,其实我们是要加而不是减的。
然后这个加减的方向正好是莫比乌斯函数mu~~

而莫比乌斯函数先线性筛出来就好了。

莫比乌斯函数:
d=1 时:
μd=1
d 可以被拆解成 k 个质因数之积且这些质因数两两不同时:
μd=(1)k
其它情况下:
μd=0

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 44723
#define inf 0x3f3f3f3f
using namespace std;
int mu[N],prime[N],cnt;
bool vis[N];
void shake()
{
    int i,j;
    mu[1]=1;
    for(i=2;i<N;i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            prime[++cnt]=i;
        }
        for(j=1;i*prime[j]<N&&j<=cnt;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j])mu[i*prime[j]]=-mu[i];
            else break;// 此处应该给mu赋0,但是天生是0就不管了
        }
    }
}
int check(int x)
{
    int i,ret=0;
    for(i=1;i*i<=x;i++)
        ret+=x/(i*i)*mu[i];
     // i^2 的倍数有x/(i*i)个
     // 容斥后贡献方向是 mu
    return ret;
}
int main()
{
    freopen("test.in","r",stdin);
    shake();
    int i,j,k,g;
    for(scanf("%d",&g);g--;)
    {
        scanf("%d",&k);
        int l=1,r=k<<1,mid; // 二分上界是有证明的
        while(l+1<r) // Orz PoPoQQQ大爷。妈呀这是什么二分这么快!!
        {
            mid=(l>>1)+(r>>1)+(l&r&1);
            if(check(mid)>=k)r=mid;
            else l=mid;
        }
        printf("%d\n",check(l)>=k?l:r);
    }
    return 0;
}
相关文章
相关标签/搜索