G. Lights

G. Lights

In the end of the day, Anna needs to turn off the lights in the office. There are $n$ lights and $n$ light switches, but their operation scheme is really strange. The switch $i$ changes the state of light $i$, but it also changes the state of some other light $a_i$ (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 $t$ ($1 \le t \le 10^4$) — the number of test cases. Descriptions of test cases follow.

The first line of each test case contains the integer $n$ ($2 \le n \le 10^5$) — the number of lights.

The second line of each test case contains the string of $n$ 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 $n$ integers $a_i$ ($1 \le a_i \le n$, $a_i \neq i$) — the switch $i$ changes the states of light $i$ and light $a_i$.

It is guaranteed that sum of $n$ over all test cases does not exceed $2 \cdot 10^5$.

Output

For each test case output the integer $k$, the minimal number of switches to use, then in the separate line output the list of $k$ switches.

If it is impossible to turn off all the lights, output single integer $-1$.

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 

 

解题思路

  由于改变第 $i$ 盏灯的状态同时也会改变第 $a_i$ 盏灯的状态,所以尝试从 $i$ 向 $a_i$ 连一条有向边,可以发现得到的图是基环树。

  很明显对于那些状态为 $1$ 且入度为 $0$ 点 $u$,必须要对第 $u$ 盏灯进行一次操作将其状态变为 $0$。因此可以先对基环树进行拓扑排序,把挂在环上的链先处理完,最后再处理环。遍历环上的每一个点并统计状态为 $1$ 的点的数量,如果这个数量是奇数那么无解(因为每次都会同时改变两盏灯的状态,因此状态为 $1$ 的点的数量也会改变偶数次,即永远不会变成偶数)。否则一定可以将环上点的状态都变成 $0$,容易知道在任意方案中每个点要么被改变一次,要么不改变。因此只需选择环中的一个点,对两种情况都模拟一边,选择改变次数最小的方案即可。

  AC 代码如下,时间复杂度为 $O(n)$:

#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

posted @ 2023-12-08 15:37  onlyblues  阅读(42)  评论(0编辑  收藏  举报
Web Analytics