#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
#include <bits/stdc++.h>
using namespace std;
#define QuocAn 0
const int MOD = 998244353;
long long power(long long base, long long exp) {
long long res = 1;
base %= MOD;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % MOD;
base = (base * base) % MOD;
exp /= 2;
}
return res;
}
long long modInverse(long long n) {
return power(n, MOD - 2);
}
void ntt(vector<int>& a, bool invert) {
int n = a.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
long long wlen = power(3, (MOD - 1) / len);
if (invert) wlen = modInverse(wlen);
for (int i = 0; i < n; i += len) {
long long w = 1;
for (int j = 0; j < len / 2; j++) {
long long u = a[i + j];
long long v = (a[i + j + len / 2] * w) % MOD;
a[i + j] = u + v < MOD ? u + v : u + v - MOD;
a[i + j + len / 2] = u - v >= 0 ? u - v : u - v + MOD;
w = (w * wlen) % MOD;
}
}
}
if (invert) {
long long n_inv = modInverse(n);
for (int& x : a) x = (x * n_inv) % MOD;
}
}
vector<int> multiply(vector<int> const& a, vector<int> const& b) {
vector<int> fa(a.begin(), a.end()), fb(b.begin(), b.end());
int n = 1;
while (n < a.size() + b.size()) n <<= 1;
fa.resize(n); fb.resize(n);
ntt(fa, false); ntt(fb, false);
for (int i = 0; i < n; i++) fa[i] = ((long long)fa[i] * fb[i]) % MOD;
ntt(fa, true);
vector<int> res(n);
for (int i = 0; i < n; i++) res[i] = fa[i];
return res;
}
const int MAXA = 1000005;
const int MAXN = 120005;
vector<int> min_prime(MAXA, 0);
vector<uint64_t> prime_hash_val(MAXA, 0);
vector<uint64_t> val_hash(MAXA, 0);
int N, K;
vector<int> adj[MAXN];
int A[MAXN];
long long W[MAXN];
long long Ans[MAXN * 3];
bool del_node[MAXN];
int sz[MAXN];
int get_sz(int u, int p) {
sz[u] = 1;
for (int v : adj[u]) {
if (v != p && !del_node[v]) {
sz[u] += get_sz(v, u);
}
}
return sz[u];
}
int get_centroid(int u, int p, int total_sz) {
for (int v : adj[u]) {
if (v != p && !del_node[v] && sz[v] * 2 > total_sz) {
return get_centroid(v, u, total_sz);
}
}
return u;
}
void get_paths(int u, int p, int d, long long w, uint64_t h, vector<tuple<uint64_t, int, long long>>& paths) {
paths.push_back({h, d, w});
for (int v : adj[u]) {
if (v != p && !del_node[v]) {
get_paths(v, u, d + 1, (w * W[v]) % MOD, h ^ val_hash[A[v]], paths);
}
}
}
void compute_pairs(vector<tuple<uint64_t, int, long long>>& paths, uint64_t H_target, long long W_C, int sign) {
if (paths.empty()) return;
sort(paths.begin(), paths.end(), [](const auto& a, const auto& b) {
return get<0>(a) < get<0>(b);
});
vector<pair<uint64_t, vector<pair<int, long long>>>> grouped;
for (const auto& p : paths) {
uint64_t h = get<0>(p);
int d = get<1>(p);
long long w = get<2>(p);
if (grouped.empty() || grouped.back().first != h) {
grouped.push_back({h, {}});
}
grouped.back().second.push_back({d, w});
}
for (auto& g : grouped) {
auto& vec = g.second;
sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
return a.first < b.first;
});
vector<pair<int, long long>> condensed;
for (const auto& p : vec) {
if (condensed.empty() || condensed.back().first != p.first) {
condensed.push_back(p);
} else {
condensed.back().second = (condensed.back().second + p.second) % MOD;
}
}
vec = condensed;
}
long long W_factor = (sign == 1) ? W_C : (MOD - W_C) % MOD;
for (int i = 0; i < grouped.size(); ++i) {
uint64_t h1 = grouped[i].first;
uint64_t h2 = h1 ^ H_target;
if (h1 > h2) continue;
auto it = lower_bound(grouped.begin(), grouped.end(), h2, [](const auto& g, uint64_t val) {
return g.first < val;
});
if (it != grouped.end() && it->first == h2) {
const auto& vecA = grouped[i].second;
const auto& vecB = it->second;
long long cur_W_factor = W_factor;
if (h1 < h2) cur_W_factor = (cur_W_factor * 2) % MOD;
long long cost_naive = (long long)vecA.size() * vecB.size();
int max_d_A = vecA.back().first;
int max_d_B = vecB.back().first;
int D = max_d_A + max_d_B + 1;
int size_D = 1; while (size_D < D) size_D <<= 1;
long long cost_ntt = (long long)size_D * __builtin_ctz(size_D) * 3;
if (cost_naive <= cost_ntt || cost_naive <= 1024) {
for (const auto& pa : vecA) {
for (const auto& pb : vecB) {
int d = pa.first + pb.first;
long long w = (pa.second * pb.second) % MOD;
w = (w * cur_W_factor) % MOD;
Ans[d] = (Ans[d] + w) % MOD;
}
}
} else {
vector<int> dense_A(max_d_A + 1, 0);
for (const auto& pa : vecA) dense_A[pa.first] = pa.second;
vector<int> dense_B(max_d_B + 1, 0);
for (const auto& pb : vecB) dense_B[pb.first] = pb.second;
vector<int> res = multiply(dense_A, dense_B);
for (int d = 0; d < res.size(); ++d) {
if (res[d]) {
long long w = ((long long)res[d] * cur_W_factor) % MOD;
Ans[d] = (Ans[d] + w) % MOD;
}
}
}
}
}
if (H_target == 0) {
for (const auto& p : paths) {
int d = get<1>(p);
long long w = get<2>(p);
long long self_w = (w * w) % MOD;
self_w = (self_w * W_factor) % MOD;
Ans[2 * d] = (Ans[2 * d] - self_w + MOD) % MOD;
}
}
}
void decompose(int u) {
int total_sz = get_sz(u, -1);
int C = get_centroid(u, -1, total_sz);
del_node[C] = true;
vector<tuple<uint64_t, int, long long>> paths;
get_paths(C, -1, 0, 1, 0, paths);
uint64_t H_target = val_hash[K] ^ val_hash[A[C]];
compute_pairs(paths, H_target, W[C], 1);
for (int v : adj[C]) {
if (!del_node[v]) {
paths.clear();
get_paths(v, C, 1, W[v], val_hash[A[v]], paths);
compute_pairs(paths, H_target, W[C], -1);
}
}
for (int v : adj[C]) {
if (!del_node[v]) {
decompose(v);
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
freopen("tuyenduong.inp" , "r" , stdin);
freopen("tuyenduong.out" , "w" , stdout);
if (!(cin >> N >> K)) return 0;
for (int i = 2; i <= N; ++i) {
int p;
cin >> p;
adj[p].push_back(i);
adj[i].push_back(p);
}
for (int i = 1; i <= N; ++i) cin >> A[i];
for (int i = 1; i <= N; ++i) cin >> W[i];
mt19937_64 rng(1337);
for (int i = 2; i <= 1000000; ++i) {
if (min_prime[i] == 0) {
prime_hash_val[i] = rng();
for (int j = i; j <= 1000000; j += i) {
if (min_prime[j] == 0) min_prime[j] = i;
}
}
}
for (int i = 2; i <= 1000000; ++i) {
int p = min_prime[i];
int temp = i;
int count = 0;
while (temp % p == 0) {
temp /= p;
count++;
}
val_hash[i] = val_hash[temp];
if (count % 2 == 1) {
val_hash[i] ^= prime_hash_val[p];
}
}
decompose(1);
long long inv2 = modInverse(2);
for (int i = 0; i < N; ++i) {
Ans[i] = (Ans[i] * inv2) % MOD;
}
Ans[0] = 0;
for (int i = 0; i < N; ++i) {
cout << Ans[i] << (i == N - 1 ? "" : " ");
}
cout << "\n";
return QuocAn;
}