1. 程式人生 > >【BZOJ2440】【中山市選2011】完全平方數 二分+容斥+莫比烏斯函式線性篩

【BZOJ2440】【中山市選2011】完全平方數 二分+容斥+莫比烏斯函式線性篩

連結:

#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~~

而莫比烏斯函式先線性篩出來就好了。

莫比烏斯函式:
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; }