开灯

GitHub跳转原题关系图返回列表

利用约数个数奇偶性,最终亮着的灯恰好是完全平方数。

OJ: luogu

题目 ID: P1876

难度:普及-

标签:数学枚举

日期: 2026-06-18 20:39

题意

N 盏灯,开始全关。 第 i 个人会把所有编号是 i 的倍数的灯切换一次状态。 问最后哪些灯是亮着的。

思路

先看一个可以直接验证想法的朴素解:

小数据时可以直接模拟每一轮翻灯。

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    vector<int> on(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            on[j] ^= 1;
        }
    }

    bool first = true;
    for (int i = 1; i <= n; i++) {
        if (!on[i]) continue;
        if (!first) cout << ' ';
        cout << i;
        first = false;
    }
    cout << '\n';

    return 0;
}

关键观察是:编号为 x 的灯会在 x 的每个约数那一轮被翻一次,所以它总共被翻的次数等于 x 的约数个数。

非完全平方数的约数成对出现,约数个数是偶数,最后还是灭的。 只有完全平方数在根号处没有成对约数,因此约数个数是奇数,最后会亮。

所以答案就是所有不超过 N 的完全平方数。

代码

cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    long long n;
    cin >> n;

    bool first = true;
    for (long long i = 1; i * i <= n; i++) {
        if (!first) cout << ' ';
        cout << i * i;
        first = false;
    }
    cout << '\n';

    return 0;
}

复杂度

只需要枚举到 N\sqrt{N},时间复杂度是 O(N)O(\sqrt{N}),空间复杂度是 O(1)O(1)

总结

这题的本质不是模拟,而是发现“亮灯编号 = 完全平方数”这个数学结论。