CSU-2172 买一送一

CSU-2172 买一送一

Description

ICPCCamp 有 n 个商店,用 1, 2, …, n 编号。对于任意 i > 1,有从商店 \(p_i\)i 的单向道路。 同时,商店 i 出售类型为 \(a_i\) 的商品。

Bobo 从商店 1 出发前往商店 i。他要在两个不同的商店购买商品(包括商店 1 和 i)。设他先购买的商品类型是 x,后购买的商品类型是 y,他用 \(f_i\) 表示不同的有序对 ⟨x, y⟩ 的数量。 求出 \(f_2, f_3, …, f_n\) 的值。

  • 1 ≤ n ≤ \(10^5\)
  • 1 ≤ \(p_i\) < i
  • 1 ≤ \(a_i\) ≤ n
  • n 的总和不超过 5 × \(10^5\).

Input

输入文件包含多组数据,请处理到文件结束。

每组数据的第一行包含 1 个整数 n.

第二行包含 (n − 1) 个整数 \(p_2, p_3, …, p_n\).

第三行包含 n 个整数 \(a_1, a_2, …, a_n\).

Output

对于每组数据输出 (n − 1) 个整数表示 \(f_2, f_3 … f_n\).

Sample Input

3
1 2
1 2 3
3
1 1
1 2 3
4
1 2 3
1 3 2 3

Sample Output

1
3
1
1
1
3
5

Hint

对于第三个样例,当 i = 4 时,可能的有序对 ⟨x, y⟩ 有 ⟨1, 2⟩,⟨1, 3⟩,⟨2, 3⟩,⟨3, 2⟩,⟨3, 3⟩ 共 5 种。所以 \(f_4 = 5\).


题解

这个题是一道搜索题,一个点的有序对数目等于它上一个点的有序对数目+到目前为止出现过的商品总数-这个点上一次出现时的有序对数目(避免重复计算),我们用一个pre数组记录一下上一个该商品出现时的商品数目,用一个vis数组记录一下一个商品是否出现过,回溯一下就可以AC了

#include<bits/stdc++.h>
#define maxn 100050
using namespace std;
vector<int> G[maxn];
int a[maxn];
long long f[maxn];
int vis[maxn];
long long pre[maxn];
void dfs(int u, int fa, int cnt) {
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		if (!vis[a[u]]) cnt++;
		vis[a[u]]++;
		f[v] = f[u] + cnt - pre[a[v]];
		int tmp = pre[a[v]];
		pre[a[v]] = cnt;
		dfs(v, u, cnt);
		vis[a[u]]--;
		if (vis[a[u]] == 0) cnt--;
		pre[a[v]] = tmp;
	}
}
int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		memset(f, 0, sizeof(f));
		memset(vis, 0, sizeof(vis));
		for (int i = 0; i <= n; i++) {
			G[i].clear();
		}
		for (int i = 2; i <= n; i++) {
			int x;
			scanf("%d", &x);
			G[x].push_back(i);
		}
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
		}
		if (n == 1) continue;
		dfs(1, 0, 0);
		for (int i = 2; i <= n; i++) {
			printf("%lld\n", f[i]);
		}
	}
	return 0;
}
/**********************************************************************
	Problem: 2172
	User: Artoriax
	Language: C++
	Result: AC
	Time:576 ms
	Memory:17568 kb
**********************************************************************/

posted @ 2019-02-04 12:17  Artoriax  阅读(230)  评论(0编辑  收藏  举报