[cf contest246] E - Blood Cousins Return

 [cf contest246] E - Blood Cousins Return

time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Polycarpus got hold of a family tree. The found tree describes the family relations of n people, numbered from 1 to n. Every person in this tree has at most one direct ancestor. Also, each person in the tree has a name, the names are not necessarily unique.

We call the man with a number a a 1-ancestor of the man with a number b, if the man with a number a is a direct ancestor of the man with a number b.

We call the man with a number a a k-ancestor (k > 1) of the man with a number b, if the man with a number b has a 1-ancestor, and the man with a number a is a (k - 1)-ancestor of the 1-ancestor of the man with a number b.

In the tree the family ties do not form cycles. In other words there isn't a person who is his own direct or indirect ancestor (that is, who is an x-ancestor of himself, for some xx > 0).

We call a man with a number a the k-son of the man with a number b, if the man with a number b is a k-ancestor of the man with a number a.

Polycarpus is very much interested in how many sons and which sons each person has. He took a piece of paper and wrote m pairs of numbers viki. Help him to learn for each pair viki the number of distinct names among all names of the ki-sons of the man with number vi.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 105) — the number of people in the tree. Next n lines contain the description of people in the tree. The i-th line contains space-separated string si and integer ri (0 ≤ ri ≤ n), where si is the name of the man with a number i, and ri is either the number of the direct ancestor of the man with a number i or 0, if the man with a number i has no direct ancestor.

The next line contains a single integer m (1 ≤ m ≤ 105) — the number of Polycarpus's records. Next m lines contain space-separated pairs of integers. The i-th line contains integers viki (1 ≤ vi, ki ≤ n).

It is guaranteed that the family relationships do not form cycles. The names of all people are non-empty strings, consisting of no more than 20 lowercase English letters.

Output

Print m whitespace-separated integers — the answers to Polycarpus's records. Print the answers to the records in the order, in which the records occur in the input.

Examples
input
6
pasha 0
gerald 1
gerald 1
valera 2
igor 3
olesya 1
5
1 1
1 2
1 3
3 1
6 1
output
2
2
0
1
0
input
6
valera 0
valera 1
valera 1
gerald 0
valera 4
kolya 4
7
1 1
1 2
2 1
2 2
4 1
5 1
6 1
output
1
0
0
0
2
0
0

题目大意:给定一棵树,给一些询问询问一个节点x子树里,与x距离为k(k不同)节点的值有多少种。

这里介绍一下dsu on tree这种东西。

这个方法是一种启发式的合并,一般与树剖合用。

思想是暴力思想的优化,与莫队思想类似。而时间复杂度的证明与轻重链(树链剖分)有关。

我先留坑吧。先放一个piano写的贴子。piano

声明一下dsu是一个离线算法,总复杂度一般是O(nlogn)的。要么就乘上一个统计的复杂度。

对于这题,我们主要思考如何统计?

用n个set维护一下就好了。复杂度是O(nlog2n)的。

code:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <map>
 5 #include <set>
 6 #include <string>
 7 #include <vector>
 8 using namespace std;
 9 
10 const int N=100005;
11 int n,q,co[N];
12 int tot,lnk[N],nxt[N],son[N];
13 int fa[N],dep[N],siz[N],got[N];
14 int skp,cnt[N],ans[N];
15 map <string,int> rel;
16 set <int> ase[N];
17 vector <pair <int,int> > a[N];
18 void add (int x,int y) {
19     nxt[++tot]=lnk[x],lnk[x]=tot;
20     son[tot]=y;
21 }
22 namespace tcd {
23     void dfs_ppw (int x,int p) {
24         fa[x]=p,dep[x]=dep[p]+1;
25         siz[x]=1,got[x]=0;
26         for (int j=lnk[x]; j; j=nxt[j]) {
27             if (son[j]==p) continue;
28             dfs_ppw(son[j],x);
29             siz[x]+=siz[son[j]];
30             if (siz[son[j]]>siz[got[x]]) got[x]=son[j];
31         }
32     }
33     void calc (int x,int v) {
34         if (v>0&&x>1) ase[dep[x]].insert(co[x]);
35         else ase[dep[x]].erase(co[x]);
36         for (int j=lnk[x]; j; j=nxt[j]) {
37             if (son[j]==fa[x]||son[j]==skp) continue;
38             calc(son[j],v);
39         }
40     }
41     void main (int x,bool f) {
42         for (int j=lnk[x]; j; j=nxt[j]) {
43             if (son[j]==fa[x]||son[j]==got[x]) continue;
44             main(son[j],1);
45         }
46         if (got[x]) main(got[x],0),skp=got[x];
47         calc(x,1),skp=0;
48         for (int i=0,c; i<a[x].size(); ++i) {
49             if (c=dep[x]+a[x][i].second>n+1) ans[a[x][i].first]=0;
50             else ans[a[x][i].first]=ase[dep[x]+a[x][i].second].size();
51         }
52         if (f) calc(x,-1);
53     }
54 }
55 int main () {
56     int x,y,k=0; char s[40];
57     scanf("%d",&n);
58     for (int i=2; i<=n+1; ++i) {
59         scanf("%s%d",s,&x),++x;
60         if (!rel[s]) rel[s]=++k;
61         co[i]=rel[s];
62         add(x,i);
63     }
64     scanf("%d",&q);
65     for (int i=1; i<=q; ++i) {
66         scanf("%d%d",&x,&y),++x;
67         a[x].push_back(make_pair(i,y));
68     }
69     dep[0]=0,siz[0]=0;
70     tcd::dfs_ppw(1,0);
71     tcd::main(1,1);
72     for (int i=1; i<=q; ++i) {
73         printf("%d\n",ans[i]);
74     }
75     return 0;
76 }
View Code

转载于:https://www.cnblogs.com/whc200305/p/8018251.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值