B. Brightness Begins
题意
有 n 个亮着的灯泡(索引从 1 到 n),对于 n 次操作op:1,2,3,… ,n,每个灯泡的索引如果能够整除 op,能 flip state,即:灯亮 ----> 灯灭 或 灯灭 ----> 灯亮。
- 对于输入 k,即最后要有 k 个灯泡亮着
- 输出:我们要找到最小的 n 满足上面条件。
一、问题转化
以 n = 12 为例,能整除 1,2,3,4,6,12,则第 12 个灯泡经过 12 次 op,最后灯是亮的,之后一直保持亮的状态。
当 n = 9 时, 能整除 1,3,9,则第 9 个灯泡经过 9 次 op,最后灯是灭的,之后一直保持灭的状态。
因此问题就变成了 n 次操作后, n 个灯至少有 k 个能被 偶数 个整数整除。
完全平方数:是指可以写成某个整数的平方的数,即其平方根为整数的数。 例如,9 = 3 × 3,它是一个平方数。 因此如果一个数是完全平方数的话,它一定是有两个一样的因子,最后会造成其能整除 奇数 个数。
最后将问题转化为 n 以内至少有 k 个非完全数 的问题。
二、代码
1. 法1
代码如下:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
// 保证上界足够大
long long k, l = 1, r = 2e18;
cin >> k;
while(r-l > 1){
long long mid = (l+r)>>1;
/**
* 完全平方数会被剔除其整数平方根,而非完全平方数则只减去了近似的整数值,从而使得 n 不再受完全平方数的影响。
*/
long long n = mid - int(sqrtl(mid));
if(n >= k) r = mid;
else l = mid;
}
cout << r << "\n";
}
return 0;
}
问题满足单调性,n 越大越有可能满足条件,因此用二分解决。
但是当我用 java 代码实现时:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt(); // Number of test cases
while (t-- > 0) {
long k = scanner.nextLong();
// 保证上界足够大
long l = 1, r = 2000000000000000000L; // 2e18;
while(l + 1 < r){
long mid = (l + r) >> 1;
/**
* 完全平方数会被剔除其整数平方根,而非完全平方数则只减去了近似的整数值,从而使得 n 不再受完全平方数的影响。
*/
long n = mid - (long)Math.sqrt(mid);
if(n >= k){
r = mid;
}else{
l = mid;
}
}
System.out.println(r);
}
scanner.close();
}
}
当 k = 854258779689358055 时,c++,java输出有所不同。
k | c++ | java |
---|---|---|
854258779689358055 | 854258780613619263 | 854258780613619262 |
按理说 java long 完全满足精度要求,不知道为什么会出错。
2. 公式法
即满足 k 个非完全数的数至少是
f
(
k
)
=
⌊
k
+
k
+
1
/
2
⌋
(1)
f(k)=\lfloor k+\sqrt{k}+1/2\rfloor \tag{1}
f(k)=⌊k+k+1/2⌋(1)
代码实现:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
long long k;
cin >> k;
cout << k + int(sqrtl(k) + 0.5) << "\n";
}
return 0;
}
总结
感觉本题还是挺有意思的。