1 条题解
-
0
自动搬运
来自洛谷,原作者为

Xy_top
constructive thinking搬运于
2025-08-24 22:44:37,当前版本为作者最后更新于2023-02-05 11:46:53,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
第一篇蓝题题解
题意简化
有 个开关分别对应 盏灯,开关的状态有两种:激活,失活。灯的状态也有两种:开,关。现在你想要进行多轮操作后把灯全部关掉,你的操作如下:先选择一个开关的位置,将它取反(原先激活变成失活,原先失活变成激活)。选择完开关位置后将所有激活的开关按下,取反它对应的灯。取反灯之后所有开关进入旋转,最后一个开关到第一个。(原先开关假设为 ,,旋转后变成 ,,)
思考算法
先来看数据范围 ,,看到灯的 状态,差不多就是状压啦。再一看 ,那肯定是预处理啦!
如果选择预处理灯的所有状态以及开关的所有状态,总状态数就是 爆炸,并且大部分的还用不到,那么我们就考虑只预处理其中一个。
问题来了:我们预处理灯的状态还是开关的状态呢?很显然是灯的状态,因为最后要求把灯都关掉,开关关不关无所谓。
深度分析
可是不难发现开关的状态对最少步数也有影响,我们考虑把开关的影响消去。并且,预处理灯的状态时需要设定一个开关的状态才可以,姑且先把开关的状态全部设成 。把这个开关叫做我的开关。
我们发现操作二和操作三是自动的,即不需要选择的。为了消除影响,要把我的开关和实际开关联系起来。
于是把 , 和 操作分开来处理。我的开关用来做 操作(它也会改变灯的状态,也会旋转。),实际开关每一轮自动做 , 操作。
但是我们仍然需要认识到对我的开关进行单点修改相当于对实际开关进行单点修改。(理解不了看完后面的再来看可能更好理解,不懂的话先忽略)
具体操作
每一轮先用实际开关, 操作改变灯。然后再看我的开关,通过 操作再改变灯,每一轮开始时我们都可以改变我的开关的一个位置的状态。此时发现我的开关初始时全为 是对的,无法对灯进行任何操作(因为我们还没做 操作),每一轮结束后进行旋转操作。
可是这样每一轮还是很棘手,我们发现对我的开关进行单点修改操作可以留着想做的时候一起做就行了,预处理一下就完事儿,预处理的东西就是灯的状态为 时,开关状态为全 0 时能否在 步之内关掉灯。时间复杂度 。没看懂?没事儿,确实有点难理解。
For example:
Example 1:
000 101初始时我们先看灯的状态为
000,开关的状态为000时,能否 步搞定(注意:步数必须要全部用完,不能少用),发现可以,答案为当前轮数减一,。Example 2:
101 100先看灯的状态为
101,开关的状态还是000时(下文省略不写),能否用 步搞定,不可以。先用实际开关操作灯,此时灯变成001,开关变成了010。第一轮的操作我们就留着了。第二轮时我们先看把之前的操作都用上能否关掉。即灯为
001时能不能一步关掉,发现可以,那么就把当初的单点修改做了。答案为轮数减一,。(注:此时若想一步用
000开关关掉001仅需要给第三个开关做单点修改。那么就相当于给实际开关第三个地方做单点修改啦!就是在第一步时把实际开关变成101)如果你觉得有一点理解了,那么再来看最后这个:
Example 3:
1000 1011可以自己先模拟一下,再来看我的过程。
首先看灯的状态为
1000能否 步搞定?不可以,那么用实际开关对灯进行操作,变成0011,实际开关旋转后变成1101,进行第二轮。第二轮开始前看一下灯的状态为
0011能否 步搞定,发现还是不行,继续操作然后旋转。灯变成1110,实际开关旋转后变成1110。第三轮之前再看下灯为
1110,能否 步搞定,可以。答案为轮数减一,。补充:方法是先单点修改第一个开关,然后灯变成
0110,开关旋转变成0100,再单点修改第三个就行。把对我的开关的操作移动到实际开关上看一看?第一轮单点修改第一个,变成0011,灯变成1011,开关旋转后变成1001,接着第二轮修改第三个,变成1011,正好搞定,我们的方法是对的。talk is cheap, show me the code:#include <iostream> using namespace std; string s,t; int T, n; int g[45][22], mask[22]; bool f[45][1 << 21]; int calc (int s, int t) { if (s == 0) return 0; for (int i = 1; i <= 2 * n; i ++) { int vis = s; for (int j = 0; j < n; j ++) if (t & (1 << j) ) vis = vis ^ g[i][j]; if (f[i][vis]) return i; } } int main() { f[0][0] = 1; cin >> T >> n; for (int i = 0; i <= n; i ++) mask[i] = 1 << i; int N = 1 << n; for (int k = 0; k < n; k ++) for (int i = 1; i <= 2 * n; i ++) for (int j = 0; j < i; j ++) g[i][k] ^= mask[(k + j) % n]; for (int i = 1; i <= 2 * n; i ++) for (int j = 0; j < N; j ++) { if (!f[i - 1][j]) continue; for (int k = 0; k < n; k ++){ int vis = j; vis ^= g[i][k]; f[i][vis] = 1; } } while (T --){ cin >> s >> t; int S = 0, T = 0; for (int i = 0; i < n; i ++) { if (s[i] == '1') S ^= (1 << i); if (t[i] == '1') T ^= (1 << i); } cout << calc(S, T) << "\n"; } return 0; }
- 1
信息
- ID
- 8306
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者