HDU 2204 容斥原理(過程詳解)
Ignatius 喜歡收集蝴蝶標本和郵票,但是Eddy的愛好很特別,他對數字比較感興趣,他曾經一度沉迷於素數,而現在他對於一些新的特殊數比較有興趣。
這些特殊數是這樣的:這些數都能表示成M^K,M和K是正整數且K>1。
正當他再度沉迷的時候,他發現不知道什麼時候才能知道這樣的數字的數量,因此他又求助於你這位聰明的程式設計師,請你幫他用程式解決這個問題。
為了簡化,問題是這樣的:給你一個正整數N,確定在1到N之間有多少個可以表示成M^K(K>1)的數。
Input
本題有多組測試資料,每組包含一個整數N,1<=N<=1000000000000000000(10^18).
Output
對於每組輸入,請輸出在在1到N之間形式如M^K的數的總數。
每組輸出佔一行。
Sample Input
10
36
1000000000000000000
Sample Output
4
9
1001003332
哎,一開始學容斥,並不知道怎麼用上去,程式碼寫起來好寫,但是思路難啊。
下面開始分析:
題目意思:給你一個n,求出1-n裡面,構成M^K(K>1)的數,比如1-4裡面有1,4兩個,1一定可以。
然後我們就去看資料範圍了,1e18,很大,不可能列舉。
接著我就想,能不能分一下類,按2^k, 3^k, 4^k.......,這種,但是一想,1e18開方也還有1e9,這樣分類不行
隨後我就按照指數來分類,指數相同歸為一類,例如(2^2, 3^2, 4^2....)歸為一類,(2^3, 3^3, 4^3)歸為一類,一次類推。
而且,這樣分類還可以算出這一類有多少個數, 直接一個公式就行了,就是開k次方,然後-1,因為是從2開始的
某一類的個數
其中n就是n,k是冪次數,比如n為8時,x^2 這一類有3個。
其實這個特別好推。
重要的是下面的: 去重
我們分析一下,如果不去重,只需要一直開方,直到不能開為止,然後答案累加,這樣複雜度並不高,1e18次最多開60次方就沒了,因為2^60=1e18,所以時間根本不用擔心。
那麼怎麼去重呢,拿樣例36來說,我們先列舉出來
指數為2: 2^2 3^2 4^2 5^2 6^2
指數為3: 2^3 3^3
指數為4:
指數為5: 2^5
後面就沒了
我們可以看到16這個數重複了,4^2 = 16, 2^4 = 16
這是因為指數4本來就是2的倍數,2^4 = ( 2^2 )^2
那如果我們指數只列舉質數,是不是就能避免這種情況了,而且又可以減小計算量
但是,稍微再列舉一下,又能發現問題,
像 27^2這種 27^2 = ( 3*3*3 )^2 = 3^(3*2) = 3^6 = 9^3
你雖然避免了3^6,但是還有9^3會重複。
這時候就是最難點了,需要用到容斥,個人感覺在這個最後一點上不好想
27^2 和 9^3之所以會重複,是因為它們都有一個6,就是都能湊出指數6,那麼我們減去指數6所得到的個數,就是答案了。
再自己一個樣例
n = 729
729 = 27^2 = 3^6
指數為2: 2^2 3^2 4^2 5^2 6^2 7^2 8^2 9^2............................27^2
指數為3: 2^3 3^3 4^3 5^3 6^3 7^3 8^3 9^3
指數為5: 2^5
指數為7: 2^7
後面沒有了
這裡,我們就挑指數為2和指數為3的兩類進行分析,看如何去除重複
我們首先要知道,指數為2和3的,重複的數一定可以構成指數為6的數
有 8^2 和 4^3是重複的
然後我們用n算出指數為6的個數,是1個,那麼在算指數為2和3時,先+指數為2的,在+指數為3的,最後--指數為6的,這就用到了容斥原理。
所以我們開始設計演算法:
①首先,我們把n的質指數求出來,質指數就是指數為質數,一個迴圈就可以搞定,而且非常快,幾下就沒了,前面分析過,就是一直開方,開到不能開為止,那麼我們就把指數記錄了下來。
②指數記錄下來了,就要用容斥了,個人習慣用dfs,簡潔一些,這時候我們還需要設計一個函式,給你一個指數值,你能算出這個符合這個指數的答案有多少個
需要注意一點,如果用dfs寫,需要加一個條件,就是因子乘積小於60,不然就會超時,因為一個很大的數得出來的指數還是挺多的,指數組合在一起也挺多,但是很多組合都沒有用,因為2^60 > 1e18,或者你加一個條件,因子最多選3個,不能選多了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define mem(a,val) memset(a,val,sizeof a)
#define inf 0x3f3f3f3f
#define longinf 0x3f3f3f3f3f3f3f3f
using namespace std;
#define eps 1e-7
typedef long long ll;
const int maxn = 1e3+4;
bool isprime[maxn];
double prime[maxn];
int primecnt;
double n;
ll ans;
ll a[maxn];
int cnt;
void getprime()
{
primecnt = 1;
mem(isprime,true);
for( int i = 4 ; i < maxn ; i+=2 )
isprime[i] = false;
for( int i = 3 ; i < maxn ; i+=2 )
if( isprime[i] )
for( int j = i<<1 ; j < maxn ; j+=i )
isprime[j] = false;
fori(2,maxn-1)
if( isprime[i] )
prime[primecnt++] = i;
}
ll f( double x )
{
ll temp = pow(n,1/x)+eps;
return temp-1;
}
void dfs( int start,int times,int goal,double val )
{
if( val > 60 )
return;
if( times == goal )
{
if( times&1 )
ans += f(val);
else ans -= f(val);
return;
}
for( int i = start ; i < cnt ; i++ )
dfs(i+1,times+1,goal,val*a[i]);
}
int main()
{
getprime();
while( scanf("%lf",&n) == 1 )
{
ans = 0;
cnt = 1;
fori(1,primecnt-1)
{
ll k = f(i);
if( k == 0 )
break;
a[cnt++] = prime[i];
}
fori(1,cnt-1)
dfs(1,0,i,1);
printf("%lld\n",ans+1);
}
return 0;
}
/*
16 4
2 5 6 9
19 3
2 3 4
*/