DTOJ2351 情报传递(message)

题目

题目描述

奈特公司是一个巨大的情报公司,它有着庞大的情报网络,情报网络中共有$n$名情报员
每名情报员可能有若干名(可能没有)下线,除$1$名大头目外其余$n-1$名情报员有且仅有$1$名上线
奈特公司纪律森严,每名情报员只能与自己的上、下线联系(???),同时,情报网络中任意两名情报员一定能够通过情报网络传递情报
奈特公司每天会派发以下两种任务中的一个任务:

  1. 搜集情报:指派T号情报员搜集情报
  2. 传递情报:将一条情报从X号情报员传递至Y号情报员

情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为$0$
一旦某个情报员开始搜集情报,他的危险值就会持续增加,每天增加$1$点危险值(开始搜集情报的当天危险值仍为$0$,第$2$天危险值为$1$,第$3$天危险值为$2$,以此类推)
传递情报并不会使情报员的危险值增加
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值$C$
奈特公司认为,参与传递这条情报的所有情报员中,危险值大于$C$的情报员将对这条情报构成威胁
现在,奈特公司希望知道,对于每个传递情报任务,参与传递的情报员有多少个,其中对这条情报构成威胁的情报员有多少个

输入格式

第$1$行包含$1$个正整数$n$,表示情报员个数
第$2$行包含$n$个非负整数,其中第$i$个整数$P_i$表示$i$号情报员上线的编号
特别地,若$P_i=0$,表示i号情报员是大头目
第$3$行包含$1$个正整数$q$,表示奈特公司将派发$q$个任务(每天一个)
随后$q$行,依次描述$q$个任务
每行首先有$1$个正整数$k$
若$k=1$,表示任务是传递情报,随后有$3$个正整数$X_i、Y_i、C_i$,依次表示传递情报的起点、终点和风险控制值
若$k=2$,表示任务是搜集情报,随后有$1$个正整数$T_i$,表示搜集情报的情报员编号

输出格式

对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开
输出不应包含多余的空行和空格

样例

样例输入

1
2
3
4
5
6
7
8
9
7
0 1 1 2 2 3 3
6
1 4 7 0
2 1
2 4
2 7
1 4 7 1
1 4 7 3

样例输出

1
2
3
5 0
5 2
5 1

数据范围与提示

样例说明

对于$3$个传递情报任务,都是经过$5$名情报员,分别是$4$号、$2$号、$1$号、$3$号和$7$号
其中,对于第$1$个任务,所有情报员(危险值为$0$)都不对情报构成威胁
对于第$2$个任务,有$2$名情报员对情报构成威胁,分别是$1$号情报员(危险值为$3$)和$4$号情报员(危险值为$2$),$7$号情报员(危险值为$1$)并不构成威胁
对于第$3$个任务,只有$1$名情报员对情报构成威胁

数据范围

对于$30\%$的数据,只包含传递情报任务
另有$30\%$的数据,保证传递情报任务中$C_i$等于$0$
对于$100\%$的数据,$n\leqslant 2\times 10^5,q\leqslant 2\times 10^5,0\leqslant P_i,C_i\leqslant n,1\leqslant T_i,X_i,Y_i\leqslant n$

题解

首先,题目相当于要求我们回答两个问题:

  1. 两个点之间的链的长度
  2. 两个点之间的链上有多少个数大于$c$

第一个问题极其简单,直接计算$dep_x+dep_y-2\times dep_{lca(x,y)}+1$就可以了
对于第二个问题,我们可以先把所有数读入,再建一棵主席树,把所有的$2$操作全部先做完,再同一回答询问
附上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<algorithm>
#include<cstdio>
using namespace std;
int n,tot,Root,head[200010],nxt[400010],to[400010],dfn[200010],nfd[200010],Fa[200010][21],dep[200010];
int m,cnt,root[200010],T[200010];
struct ppap
{
int a,b,c,d;
}s[200010];
struct Segment_Tree
{
int ls,rs,sum;
}t[4000010];
void add(int u,int v)
{
nxt[++tot]=head[u],head[u]=tot,to[tot]=v;
}
void dfs(int x,int fa)
{
dfn[x]=++cnt,nfd[cnt]=x,Fa[x][0]=fa,dep[x]=dep[fa]+1;
for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa) dfs(to[i],x);
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--) if(dep[x]-(1<<i)>=dep[y]) x=Fa[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--) if(Fa[x][i]!=Fa[y][i]) x=Fa[x][i],y=Fa[y][i];
return Fa[x][0];
}
void change(int &p,int fa,int l,int r,int d)
{
if(!p) p=++cnt;
if(l==r){t[p].sum=t[fa].sum+1;return;}
int mid=(l+r)>>1;
if(d<=mid) change(t[p].ls,t[fa].ls,l,mid,d),t[p].rs=t[fa].rs;
else change(t[p].rs,t[fa].rs,mid+1,r,d),t[p].ls=t[fa].ls;
t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
}
int ask(int b,int c,int LCA,int fa,int l,int r,int L,int R)
{
if(L<=l&&R>=r) return t[b].sum+t[c].sum-t[LCA].sum-t[fa].sum;
int mid=(l+r)>>1,ans=0;
if(L<=mid) ans=ask(t[b].ls,t[c].ls,t[LCA].ls,t[fa].ls,l,mid,L,R);
if(R>mid) ans+=ask(t[b].rs,t[c].rs,t[LCA].rs,t[fa].rs,mid+1,r,L,R);
return ans;
}
int main()
{
freopen("message.in","r",stdin);
freopen("message.out","w",stdout);
scanf("%d",&n);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
if(!x) Root=i;
else add(x,i),add(i,x);
}
dfs(Root,0);
for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) Fa[i][j]=Fa[Fa[i][j-1]][j-1];
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&s[i].a,&s[i].b);
if(s[i].a==1) scanf("%d%d",&s[i].c,&s[i].d);
if(s[i].a==2) T[s[i].b]=i;
}
for(int i=1;i<=n;i++) change(root[nfd[i]],root[Fa[nfd[i]][0]],0,m,T[nfd[i]]);
for(int i=1;i<=m;i++){
if(s[i].a==2) continue;
int LCA=lca(s[i].b,s[i].c);
printf("%d ",dep[s[i].b]+dep[s[i].c]-dep[LCA]*2+1);
if(i-s[i].d-1<1) printf("0\n");
else printf("%d\n",ask(root[s[i].b],root[s[i].c],root[LCA],root[Fa[LCA][0]],0,m,1,i-s[i].d-1));
}
}