1. 程式人生 > >京東非常精彩的冪數論題目 京東算法題---求冪

京東非常精彩的冪數論題目 京東算法題---求冪

標記 n) n-1 ava 累加 sqrt hash ins clas

//這個才是正確的代碼
作者:牛妹
鏈接:https://www.nowcoder.com/discuss/38889?type=0&order=3&pos=6&page=1
來源:牛客網

我們考慮去枚舉n範圍內的所有i,然後處理出i的冪那些數。 這個i就叫做底.

因為a^b=c^d 那麽必然存在一個i s.t.下面的十字成立.這個證明做因數分解即可.
考慮對於i ^ x, 我們需要計算滿足 (i ^ x) ^ c = (i ^ y) ^ d的數量,其中i ^ x, i ^ y <= n. 這些我們可以通過預處理出來。


然後對於(i ^ x) ^ c = (i ^ y) ^ d 其實意味著x c = y d, 意味著(x / y) = (d / c),(因為c,d可以做到互素) 其中x, y我們可以在預處理之後枚舉出來,於是我們就可以借此計算出n範圍內有多少不同這種c和d去滿足等式。
其實就等於 n / max(x / gcd(x, y), y / gcd(x, y)),然後都累加進答案。gcd()表示最大公約數。
中間可能產生重復枚舉,我們用一個set或者hash容器標記一下就好。

以上枚舉對於2~sqrt(n)。最後對於大於sqrt(n)的部分,每個的貢獻都是n。


import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {

public final static long MOD = 1000000000 + 7;

public static int max(int a, int b){
return (a>b) ? a : b;
}

public static long gcd(long a,long b){
return (a % b == 0) ? b : gcd(b,a%b);
}

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
long n = in.nextInt();
long ans = (long)1*n*(2*n-1) % MOD;//以1為底的個數,這個計算方式是 1.計算1**x=1**y 有n**2個 2.計算x**y=x**y這個有 n**2-n個 減去這個n表示第一種1裏面1為底的已經算過了.
//3.所以下面討論的情況是(i ^ x) ^ c = (i ^ y) ^ d且x!=y且i>1 這種情況.所以i從2取到根號n.

Set<Integer> set = new HashSet<>();
for (int i = 2; i*i <= n; i++){ //下面的底至少是2,指數至少也是2,所以i**2<=n
if ( set.contains(i)) continue;
long tmp = i;
int cnt = 0;

while(tmp <= n) {
set.add((int)tmp);
tmp = tmp * i;
cnt++;
}//比如i取2,n=10的時候,那麽set就是{2,4,8} cnt=3,之後4,8就不用算了,因為他們屬於2這個底
//cnt表示最高能取多少次冪.
for(int k = 1; k <= cnt; k++) {
for(int j = k + 1; j <= cnt; j++) {
ans = (ans + n / (j / gcd(k, j) ) * (long)2 ) % MOD;
//(j / gcd(k, j) )這個操作就是把k,j這個分數變成互素的.
//比如k取1,j取2的時候,ans=ans+10/2*2:因為(i ^ x) ^ c = (i ^ y) ^ d且x!=y且i>1 所以這時也就是 (i ^ 1) ^ c = (i ^ 2) ^ d ,那麽d最高取5個數分別是1到5,因為c<=10
//又如k=2,j=3, (i ^ 2) ^ c = (i ^ 3) ^ d ,那麽d最高取3個分別是2,4,6,因為c<=10
//這個地方為什麽這麽計算呢因為k,j互素化之後,為了乘積xk=jy,那麽x必須是j的倍數,但是x必須小於n
//所以x的取法只有n/j種.(這時j已經是(j / gcd(k, j) )了!)
}
}

}

System.out.println(ans);
}
}

京東非常精彩的冪數論題目 京東算法題---求冪