[SCOI2015] 情报传递
题目描述
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有 n 名情报员。每名情报员可能有若干名 (可能没有) 下线,除 1 名大头目外其余 n−1 名情报员有且仅有 1 名上线。奈特公司纪律森严,每名情报员只能与自己的上、下线联系,同时,情报网络中任意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务:
- 搜集情报:指派 T 号情报员搜集情报;
- 传递情报:将一条情报从 X 号情报员传递给 Y号情报员。
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为 0 ;一旦某个情报员开始搜集情报,他的危险值就会持续增加,每天增加 1点危险值 (开始搜集情报的当天危险值仍为 0 ,第 2 天危险值为 1 ,第 3 天危险值为 2 ,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值 C 。奈特公司认为,参与传递这条情报的所有情报员中,危险值大于 C 的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
Input
第一行包含一个正整数 n ,表示情报员个数。 笫二行包含 n 个非负整数,其中第 i 个整数 Pi 表示 i 号情报员上线的编号。特别地,若 Pi=0 ,表示 i 号情报员是大头目。 第三行包含一个正整数 q ,表示奈特公司将派发 q 个任务 (每天一个)。
随后 q 行,依次描述 q 个任务。每行首先有一个正整数 kk 。
- 若 k=1 ,表示任务是传递情报,随后有三个正整数 Xi 、Yi 、Ci ,依次表示传递情报的起点、终点和风险控制值。
- 若 k=2 ,表示任务是搜集情报,随后有 1 个正整数 Ti ,表示搜集情报的情报员编号。
Output
对于每个传递情报任务输出一行,包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
Hint
\(n\leqslant 2\times 10^5,Q\leqslant 2\times 10^5,0<P_i,C_i\leqslant N,1\leqslant T_i,X_i,Y_i\leqslant n\)
Solution
将收集情报看作染色操作,那么对于编号为 \(idx\) 的询问,显然要求的是在 \(idx-c-1\) 或更早这一条链上有多少点被染过色。
容易想到离线处理,按照 \(idx-c-1\) 从小到大排序,并实时树上染色。
对于求两点距离,显然可以用两点深度之和再减去 \(lca\) 的深度的二倍,求 \(lca\) 可以在做第二问时顺便求出,所以我们返回一个 \(pair\) 类型即可。
Code
#include<cstdio>
#include<cctype>
#include<algorithm>
#define N 200005
#define blank putchar(' ')
#define nxtline putchar('\n')
#define max(A,B) ((A)>(B)?(A):(B))
#define min(A,B) ((A)<(B)?(A):(B))
#define swap(A,B) ((A)^=(B)^=(A)^=(B))
int ques[N][5],q;
int n,m,cnt,pos,tot;
int sze[N],son[N],fa[N];
int sum[N<<2],ans[N],d[N];
int head[N],dfn[N],top[N];
struct Node{
int x,y,z;
int idx,ans1,ans2;
}node[N];
struct Edge{
int to,nxt;
}edge[N];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
bool cmp1(Node a,Node b){
return a.idx-a.z<b.idx-b.z;
}
bool cmp2(Node a,Node b){
return a.idx<b.idx;
}
int getint(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
void write(int x){
if(x>9) write(x/10);
putchar(x%10+'0');
}
void first_dfs(int now){
sze[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
d[to]=d[now]+1;
fa[to]=now;
first_dfs(to);
sze[now]+=sze[to];
if(sze[to]>sze[son[now]])
son[now]=to;
}
}
void second_dfs(int now,int low){
top[now]=low;
dfn[now]=++tot;
if(son[now])
second_dfs(son[now],low);
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==son[now])
continue;
second_dfs(to,to);
}
}
void pushup(int cur){
sum[cur]=sum[cur<<1]+sum[cur<<1|1];
}
void modify(int cur,int l,int r,int ql,int qr){
if(l==r){
sum[cur]=1;
return;
}
int mid=l+r>>1;
if(ql<=mid)
modify(cur<<1,l,mid,ql,qr);
else
modify(cur<<1|1,mid+1,r,ql,qr);
pushup(cur);
}
int query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr)
return sum[cur];
int mid=l+r>>1,ans=0;
if(ql<=mid)
ans+=query(cur<<1,l,mid,ql,qr);
if(mid<qr)
ans+=query(cur<<1|1,mid+1,r,ql,qr);
return ans;
}
std::pair<int,int> ask(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
ans+=query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(d[x]<d[y])
swap(x,y);
ans+=query(1,1,n,dfn[y],dfn[x]);
return std::make_pair(y,ans);
}
signed main(){
n=getint();int root;
for(int i=1;i<=n;i++){
int p=0;
if((p=getint())==0)
root=i;
else
add(p,i);
}
first_dfs(root);second_dfs(root,root);
m=getint();
for(int i=1;i<=m;i++){
if(getint()==1){
node[++pos].x=getint();
node[pos].y=getint();
node[pos].z=getint();
node[pos].idx=i;
} else{
ques[++q][1]=getint();
ques[q][2]=i;
}
}
std::sort(node+1,node+1+pos,cmp1);
int now=0;
for(int i=1;i<=pos;i++){
while(now<q and node[i].idx-node[i].z>ques[now+1][2]){
now++;
modify(1,1,n,dfn[ques[now][1]],dfn[ques[now][1]]);
}
std::pair<int,int> p=ask(node[i].x,node[i].y);
node[i].ans1=d[node[i].x]+d[node[i].y]+1-2*d[p.first];
node[i].ans2=p.second;
}
std::sort(node+1,node+1+pos,cmp2);
for(int i=1;i<=pos;i++){
write(node[i].ans1);blank;
write(node[i].ans2);nxtline;
}
return 0;
}