【BZOJ2440】【中山市選2011】完全平方數 二分+容斥+莫比烏斯函式線性篩
阿新 • • 發佈:2019-01-07
連結:
#include <stdio.h>
int main()
{
puts("轉載請註明出處[vmurder]謝謝");
puts("網址:blog.csdn.net/vmurder/article/details/44646027");
}
題解:
首先整體思想上我們可以二分check前x個數中有多少個符合要求的數。
然後這個怎麼check呢?發現我們列舉每個數,看範圍內是它的平方的倍數的數有多少個就行了。然後發現容斥一下,有些數是要加的,而有些數是要減的,比如列舉到3,那麼3*3*4=36被刪了一次,而列舉到2時其實2*2*9=36就已經被刪了一次啦。所以在列舉2*3=6時,其實我們是要加而不是減的。
然後這個加減的方向正好是莫比烏斯函式mu~~
而莫比烏斯函式先線性篩出來就好了。
莫比烏斯函式:
當
當
其它情況下:
程式碼:
#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;
}