Java实现整数N多进制前缀为1统计时循环异常排查
问题排查与代码修复
咱们先一步步拆解你代码里的问题,看看为什么只处理第一个测试用例就出异常了:
核心Bug分析
1. 原始输入n被意外修改
你在进制转换的内层while(temp>0)循环里,直接对输入的n做了除法和取余操作,把n改成了0。这就导致:
- 第一个测试用例处理到后续进制
j时,n已经是0,完全没法正确计算 - 后续测试用例读取新的
n后,同样会被这个循环修改成0,逻辑彻底混乱
解决办法:用临时变量存储原始n,比如long temp = n;,只对temp做进制转换操作。
2. count变量未重置
你把count定义在所有测试用例循环外面,第一个测试用例的计数会累加到后面的测试用例里,导致结果完全错误。
解决办法:在每个测试用例开始时(也就是for循环内部)把count重置为0。
3. 最高位判断逻辑错误
你的代码里判断rem[1]==1,但rem数组存储的是从低位到高位的数字(比如6在2进制是110,rem[0]是0,rem[1]是1,rem[2]是1)。正确的判断应该是检查最高位是否为1,也就是rem[index-1] == 1。
4. 特殊情况未处理
- 当
N=1时,所有大于1的进制下,1的表示都是1,有无穷多进制,应该输出INFINITY - 当
N=0时,所有进制下都是0,没有以1开头的情况,计数为0
修复后的基础代码(解决循环异常)
先解决循环异常的问题,代码如下:
import java.util.*; public class MyClass { public static void main(String args[]) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); for(int i=0;i<t;i++) { long n = sc.nextLong(); // 用nextLong避免1e12的溢出问题 long count = 0; // 处理特殊情况 if(n == 1) { System.out.println("INFINITY"); continue; } if(n == 0) { System.out.println(0); continue; } long j=2; while(j<=n) { long temp = n; // 用临时变量保护原始输入n int index=0; long[] rem = new long[60]; // 1e12在2进制最多40位,60足够 while(temp>0) { long rem1 = temp%j; rem[index++] = rem1; temp = temp / j; } // 检查最高位是否为1 if(index > 0 && rem[index-1] == 1) { count++; } j++; } System.out.println(count); } sc.close(); } }
效率优化(必做!否则大N会超时)
上面的代码虽然解决了循环异常,但当N达到1e12时,遍历从2到N的所有进制会直接超时(尤其是T=1e5的情况下)。我们可以用数学思路优化:
当N在b进制下以1开头,意味着存在整数k≥1,满足:b^k ≤ N < 2*b^k
我们分两种情况统计:
- k=1:此时
b的范围是floor(N/2)+1到N,共有N - floor(N/2)个值 - k≥2:遍历k从2到
log2(N)(最多40次,因为2^40≈1e12),对每个k计算b的上下界:(N/2)^(1/k) < b ≤ N^(1/k),统计其中的整数数量
优化后的高效代码
import java.util.*; public class MyClass { public static void main(String args[]) { Scanner sc = new Scanner(System.in); int t = sc.nextInt(); while(t-- >0) { long n = sc.nextLong(); if(n == 1) { System.out.println("INFINITY"); continue; } if(n == 0) { System.out.println(0); continue; } long count = 0; // 情况1:k=1,b的范围是 (n/2, n] count += n - (n/2); // 情况2:k≥2,遍历k直到high<2(b至少为2) for(int k=2; ;k++) { long low = (long)Math.pow(n/2.0, 1.0/k) + 1; long high = (long)Math.pow(n, 1.0/k); if(high < 2) break; // 没有符合条件的b,退出循环 low = Math.max(low, 2); // 确保b不小于2 if(low <= high) { count += high - low + 1; } } System.out.println(count); } sc.close(); } }
这个代码的时间复杂度是O(T*logN),完全可以处理1e5个测试用例和1e12以内的N。
内容的提问来源于stack exchange,提问作者Virti Parekh




