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;
}