原题跳转
在std上加了些注释,方便理解
using namespace std;
typedef long long ll;
const int maxn = 2005;
const int N = 2000;
const int b = 19560929;
const int mod1 = 1000000007;
const int mod2 = 1000000009;
// n
// ans: 当前路径上的权值的种类数
// w数组: 记录每个点的权值
// a数组: 记录本行aij的值
// cnt数组: 记录在当前路径中某权值出现的次数
// m1数组: 记录公式中给出的x的j-1次方 % mod1的值
// m2数组: 记录公式中给出的x的j-1次方 % mod2的值
ll n, ans, w[maxn], a[maxn], cnt[maxn], m1[maxn], m2[maxn];
vector<int> g[maxn]; // 邻接表
void init(int n){
m1[0] = m2[0] = 1;
for (int i = 1; i <= n; i++) {
m1[i] = m1[i - 1] * b % mod1;
m2[i] = m2[i - 1] * b % mod2;
}
}
void fadd1(ll &x, ll y){
x += y;
x %= mod1;
}
void fadd2(ll &x, ll y){
x += y;
x %= mod2;
}
void add(int x){
// 如果当前x权值在路径上未出现过, 则种类数++
if(!cnt[x]) ans++;
// 当前权值的数量++
cnt[x]++;
}
void del(int x){
// 如果当前x权值在路径上只出现过一次,
// 则回溯时, 这个权值将消失, 则种类数--
if(cnt[x] == 1) ans--;
// 当前权值的数量--
cnt[x]--;
}
void dfs(int x, int pre){
// 将当前权值加入计数
add(w[x]);
// aij的值, 即到达本层的权值种类数
a[x] = ans;
for(int v: g[x]){
// 如果是父节点则跳过
if(v != pre){
dfs(v, x);
}
}
// 回溯
del(w[x]);
}
void solve(){
scanf("%d", &n);
int x;
// 创建邻接表
for (int i = 2; i <= n; ++i) {
scanf("%d", &x);
g[x].push_back(i);
g[i].push_back(x);
}
// 读入权值
for (int i = 1; i <= n; ++i) {
scanf("%d", &w[i]);
}
// 求每一行的结果
for (int i = 1; i <= n; ++i) {
// dfs求出本行的aij
dfs(i, -1);
ll h1 = 0, h2 = 0;
// 求出f1,f2
for (int j = 1; j <= n; ++j) {
fadd1(h1, a[j] * m1[j - 1] % mod1);
fadd2(h2, a[j] * m2[j - 1] % mod2);
}
printf("%lld %lld\n", h1, h2);
}
// 清除邻接表, 供下一次使用
for (int i = 1; i <= n; ++i) {
g[i].clear();
}
}
int main(){
int t;
init(N - 1);
scanf("%d", &t);
while (t--) solve();
return 0;
}