G. Lights
G. Lights
In the end of the day, Anna needs to turn off the lights in the office. There are lights and light switches, but their operation scheme is really strange. The switch changes the state of light , but it also changes the state of some other light (change the state means that if the light was on, it goes off and vice versa).
Help Anna to turn all the lights off using minimal number of switches, or say it is impossible.
Input
The first line of input contains a single integer () — the number of test cases. Descriptions of test cases follow.
The first line of each test case contains the integer () — the number of lights.
The second line of each test case contains the string of characters, the initial state of the lights. Character "0" means that the corresponding light is off, and "1" means that it is on.
The third line of each test case contains integers (, ) — the switch changes the states of light and light .
It is guaranteed that sum of over all test cases does not exceed .
Output
For each test case output the integer , the minimal number of switches to use, then in the separate line output the list of switches.
If it is impossible to turn off all the lights, output single integer .
Example
input
8
5
11101
4 3 4 2 2
2
10
2 1
10
0000000011
9 10 10 7 10 9 9 9 10 2
10
1000111101
9 3 8 9 2 1 3 7 2 7
10
0001101010
5 7 6 10 8 3 6 6 2 2
10
0101100010
8 7 7 9 9 4 1 4 2 7
10
1010111010
7 9 10 7 7 2 8 6 10 4
10
1110000001
3 10 10 1 10 8 6 3 2 1
output
3
1 5 3
-1
1
9
5
5 6 10 2 3
6
4 9 5 10 8 7
3
5 4 9
6
1 3 5 9 7 8
2
2 1
解题思路
由于改变第 盏灯的状态同时也会改变第 盏灯的状态,所以尝试从 向 连一条有向边,可以发现得到的图是基环树。
很明显对于那些状态为 且入度为 点 ,必须要对第 盏灯进行一次操作将其状态变为 。因此可以先对基环树进行拓扑排序,把挂在环上的链先处理完,最后再处理环。遍历环上的每一个点并统计状态为 的点的数量,如果这个数量是奇数那么无解(因为每次都会同时改变两盏灯的状态,因此状态为 的点的数量也会改变偶数次,即永远不会变成偶数)。否则一定可以将环上点的状态都变成 ,容易知道在任意方案中每个点要么被改变一次,要么不改变。因此只需选择环中的一个点,对两种情况都模拟一边,选择改变次数最小的方案即可。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
char s[N];
int ne[N], deg[N];
int q[N];
void solve() {
int n;
scanf("%d %s", &n, s + 1);
memset(deg, 0, n + 10 << 2);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
ne[i] = x;
deg[x]++;
}
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++) {
if (!deg[i]) q[++tt] = i;
}
vector<int> ans;
while (hh <= tt) {
int u = q[hh++];
if (s[u] & 1) {
s[u] ^= 1;
s[ne[u]] ^= 1;
ans.push_back(u);
}
if (--deg[ne[u]] == 0) q[++tt] = ne[u];
}
for (int i = 1; i <= n; i++) {
if (deg[i]) {
int u = i, cnt = 0, sum = 0;
while (deg[u]) {
deg[u] = 0;
cnt++; // 统计环中点的数量
sum += s[u] & 1; // 统计状态为1的点的数量
u = ne[u];
}
if (sum & 1) { // sum是奇数则无解
printf("-1\n");
return;
}
vector<int> p1, p2;
u = i; // 任选环中一个点
for (int j = 0, t = 0; j < cnt; j++) { // 这个点改变一次
if (!j || (t ^ s[u]) & 1) {
t = 1;
p1.push_back(u);
}
else {
t = 0;
}
u = ne[u];
}
u = i;
for (int j = 0, t = 0; j < cnt; j++) { // 这个点不改变
if (j && (t ^ s[u]) & 1) {
t = 1;
p2.push_back(u);
}
else {
t = 0;
}
u = ne[u];
}
if (p1.size() > p2.size()) p1.swap(p2); // 选择改变次数最小的方案
for (auto &x : p1) {
ans.push_back(x);
}
}
}
printf("%d\n", ans.size());
for (auto &x : ans) {
printf("%d ", x);
}
printf("\n");
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 913 (Div. 3) Editorial:https://codeforces.com/blog/entry/123012
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17888276.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效