文章

【GESP】C++三级练习 luogu-P1161 开灯

GESP C++三级练习,一维数组练习,难度★★☆☆☆。

luogu-P1161 开灯

题目要求

题目描述

在一条无限长的路上,有一排无限长的路灯,编号为 $1,2,3,4,\dots$。

每一盏灯只有两种可能的状态,开或者关。如果按一下某一盏灯的开关,那么这盏灯的状态将发生改变。如果原来是开,将变成关。如果原来是关,将变成开。

在刚开始的时候,所有的灯都是关的。小明每次可以进行如下的操作:

指定两个数,$a,t$($a$ 为实数,$t$ 为正整数)。将编号为 $\lfloor a\rfloor,\lfloor 2 \times a\rfloor,\lfloor3 \times a\rfloor,\dots,\lfloor t \times a\rfloor$ 的灯的开关各按一次。其中 $\lfloor k \rfloor$ 表示实数 $k$ 的整数部分。

在小明进行了 $n$ 次操作后,小明突然发现,这个时候只有一盏灯是开的,小明很想知道这盏灯的编号,可是这盏灯离小明太远了,小明看不清编号是多少。

幸好,小明还记得之前的 $n$ 次操作。于是小明找到了你,你能帮他计算出这盏开着的灯的编号吗?

输入格式

第一行一个正整数 $n$,表示 $n$ 次操作。

接下来有 $n$ 行,每行两个数,$a_i,t_i$。其中 $a_i$ 是实数,小数点后一定有 $6$ 位,$t_i$ 是正整数。

输出格式

仅一个正整数,那盏开着的灯的编号。

输入输出样例 #1

输入 #1

1
2
3
4
3
1.618034 13
2.618034 7
1.000000 21

输出 #1

1
20

说明/提示

记 $T=\sum \limits_{i=1}^n t_i = t_1+t_2+t_3+\dots+t_n$。

  • 对于 $30\%$ 的数据,满足 $T \le 1000$;
  • 对于 $80\%$ 的数据,满足 $T \le 200000$;
  • 对于 $100\%$ 的数据,满足 $T \le 2000000$;
  • 对于 $100\%$ 的数据,满足 $n \le 5000$,$1 \le a_i<1000$,$1 \le t_i \le T$。

数据保证,在经过 $n$ 次操作后,有且只有一盏灯是开的,不必判错。而且对于所有的 $i$ 来说,$t_i\times a_i$ 的最大值不超过 $2000000$。


题目分析

解题思路

本题的解题思路如下:

  1. 问题本质:
    • 给定一系列操作,每次操作会按指定规则改变一些灯的状态
    • 每个操作包含两个参数:实数 $a$ 和正整数 $t$
    • 每次操作会改变编号为 $\lfloor a \rfloor$, $\lfloor 2 \times a \rfloor$, …, $\lfloor t \times a \rfloor$ 的灯的状态
    • 最终只有一盏灯是亮着的,需要找出这盏灯的编号
  2. 解题关键:
    • 核心思路 - 状态模拟:
      1. 使用数组记录每盏灯的状态:
        • 创建一个足够大的数组 $array$(根据题目提示 $2000005$ 大小足够)
        • $0$ 表示灯是关闭的,$1$ 表示灯是打开的
      2. 模拟每次操作:
        • 对于每次操作的参数 $a$ 和 $t$
        • 计算需要改变状态的灯的编号:$\lfloor i \times a \rfloor$ ($i$ 从 $1$ 到 $t$)
        • 使用异或操作($\oplus 1$)切换对应灯的状态
      3. 最后遍历数组,找到值为 $1$ 的位置,即为答案
  3. 复杂度分析:
    • 时间复杂度:$O(n \times t_{max})$,其中n为操作次数,t_max为单次操作最大次数
    • 空间复杂度:$O(2000005)$,需要一个固定大小的数组存储灯的状态


示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

// 用于存储每个灯的状态的数组,0表示关闭,1表示打开
int array[2000005];
int main() {
    // n表示操作次数
    int n;
    std::cin >> n;
    // 循环处理每次操作
    for (int i=0; i < n; i++) {
        // a为实数倍数,t为操作次数上限
        double a;
        int t;
        std::cin >> a >> t;
        // 对于每次操作,计算需要改变状态的灯的编号
        for (int j = 1; j <=t; j++) {
            // 计算当前需要改变状态的灯的编号
            int idx = a * j;
            // 使用异或操作切换灯的状态(0变1,1变0)
            array[idx] ^= 1;
        }
    }
    // 遍历所有可能的灯,找出唯一一个打开的灯
    for (int i=0; i < 2000005; i++) {
        // 如果找到值为1的位置,说明这盏灯是打开的
        if (array[i] == 1) {
            std::cout << i << " ";
        }
    }
    return 0;
}             

所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code

GESP各级别考纲、真题讲解、知识拓展和练习清单等详见【置顶】【GESP】C++ 认证学习资源汇总

luogu-”系列题目已加入洛谷Java、C++初学团队作业清单,可在线评测,团队名额有限。

bcqm-”系列题目可在编程启蒙题库进行在线评测。

欢迎加入Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答

欢迎加入C++ GESP/CSP认证学习QQ频道,考试资源总结汇总

欢迎加入C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助

GESP/CSP 认证学习微信公众号
GESP/CSP 认证学习微信公众号
本文由作者按照 CC BY 4.0 进行授权