樱子奇怪の小爱好(Codeforces Round 970 (Div. 3)D)

D. Sakurako's Hobby

对于某个排列 \(p\) \(^{\text{∗}}\) 如果可以通过赋值 \(i=p_i\) 一定次数使 \(i\) 等于 \(j\) ,则樱子称整数 \(j\) 可以从整数 \(i\) 到达

如果是 \(p=[3,5,6,1,2,4]\) ,那么,举例来说, \(4\) 可以从 \(1\) 到达,因为: \(i=1\) \(\rightarrow\) \(i=p_1=3\) \(\rightarrow\) \(i=p_3=6\) \(\rightarrow\) \(i=p_6=4\) .现在是 \(i=4\) ,所以 \(4\) 可以从 \(1\) 到达

排列中的每个数字都被染成黑色或白色

樱子将函数 \(F(i)\) 定义为从 \(i\) 可以得到的黑色整数的个数

樱子对每个 \(1\le i\le n\)\(F(i)\) 都很感兴趣,但计算所有值变得非常困难,因此她请你作为她的好朋友来计算这个值

\(^{\text{∗}}\) 长度为 \(n\) 的排列是由 \(n\) 个不同的整数组成的数组,这些整数从 \(1\)\(n\) 按任意顺序排列。例如, \([2,3,1,5,4]\) 是一个排列,但 \([1,2,2]\) 不是一个排列(数字 \(2\) 在数组中出现了两次), \([1,3,4]\) 也不是一个排列( \(n=3\) ,但数组中包含 \(4\)

INPUT

第一行包含一个整数 \(t\) ( \(1\le t\le 10^4\) ) - 测试用例数

每个测试用例的第一行包含一个整数 \(n\) ( \(1\le n\le 2\cdot 10^5\) ) - 数组中的元素个数

每个测试用例的第二行包含 \(n\) 个整数 \(p_1, p_2, \dots, p_n\) ( \(1\le p_i\le n\) ) - 排列元素

每个测试用例的第三行包含一个长度为 \(n\) 的字符串 \(s\) ,由 "0 "和 "1 "组成。如果 \(s_i=0\) ,则数字 \(p_i\) 被涂成黑色;如果 \(s_i=1\) ,则数字 \(p_i\) 被涂成白色

保证所有测试用例中 \(n\) 的总和不超过 \(2\cdot 10^5\)

OUTPUT

对于每个测试用例,输出 \(n\) 个整数 \(F(1), F(2), \dots, F(n)\)

Example

Input

5
1
1
0
5
1 2 4 5 3
10101
5
5 4 1 3 2
10011
6
3 5 6 1 2 4
010000
6
1 2 3 4 5 6
100110

Output

1 
0 1 1 1 1 
2 2 2 2 2 
4 1 4 4 1 4 
0 1 1 0 0 1 

[!TIP]

歌词大意:哼~ 哎呀呀,给了个长度是n的排列数组p还有长度也是n的颜色字符串s啦,真是的 ~ ❤要人家去算从每个位置能到达的所有位置里黑色数字的数量呢,哼!那个黑色数字就是字符串s里面的字符'0'表示的啦,真麻烦呀,不过杂鱼大哥哥不要认输呢,杂鱼大哥哥快点好好算一算啦,嘻嘻 ~ 杂鱼~ ❤❤❤

好像没用到动态规划的说❤❤❤,想上传那种图

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;

int f[N], g[N], fumo[N],p[N], t, n;//p[i]表示在排列中第 i 个位置上的值(从位置 i 可以到达的下一个位置)
char s[N];

// 查找根节点❤❤❤
int find(int x) {
    if (f[x]!= x) {
        f[x] = find(f[x]);
    }
    return f[x];
}
// 合并两个集合❤❤❤
void baka(int x, int y) {
    int findX = find(x);
    int findY = find(y);

    if (findX!= findY) {
        if (g[findX] > g[findY]) {
            f[findY] = findX;
            fumo[findX] += fumo[findY];
        } else {
            f[findX] = findY;
            fumo[findY] += fumo[findX];
        }
    }
}

int main() {
    scanf("%d", &t);

    while (t--) {
        scanf("%d", &n );
		//排列数组❤❤❤
        for (int i = 0; i < n; i++) {
            scanf("%d", &p[i]);
            p[i]--; //转换为0索引❤❤❤
        }
        scanf("%s", s);
        // 初始化并查集❤❤❤
        for (int i = 0; i < n; i++) {
            f[i] = i;
            g[i] = 0;
            fumo[i] = 0;
        }

        // 初始化黑色数字计数❤❤❤
        for (int i = 0; i < n; i++) {
            if (s[i] == '0') {
                fumo[find(i)]++;
            }
        }
        //构建并查集❤❤❤
        for (int i = 0; i < n; i++) {
            baka(i, p[i]);//合并当前节点和排列数组中对应的节点❤❤❤
        }

        for (int i = 0; i < n; i++) {
            printf("%d ", fumo[find(i)]);//啊~出来了~~~❤❤❤❤❤❤❤
        }
        printf("\n");
    }
    return 0;
}




posted @ 2024-11-27 17:09  土木牢盖  阅读(39)  评论(0)    收藏  举报