BZOJ4448[Scoi2015]情报传递——主席树+LCA
题目描述
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有n名情报员。每名情报员口J-能有
若T名(可能没有)下线,除1名大头目外其余n-1名情报员有且仅有1名上线。奈特公司纪律森严,每
名情报员只能与自己的上、下线联系,同时,情报网络中仟意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务:
1.搜集情报:指派T号情报员搜集情报
2.传递情报:将一条情报从X号情报员传递给Y号情报员
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为0;-旦某个情报员开
始搜集情报,他的危险值就会持续增加,每天增加1点危险值(开始搜集情报的当天危险值仍为0,第2天
危险值为1,第3天危险值为2,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值C。奈特公司认为,参与传递这条情
报的所有情报员中,危险值大于C的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每
个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
输入
第1行包含1个正整数n,表示情报员个数。
笫2行包含n个非负整数,其中第i个整数Pi表示i号情报员上线的编号。特别地,若Pi=0,表示i号
情报员是大头目。
第3行包含1个正整数q,表示奈特公司将派发q个任务(每天一个)。
随后q行,依次描述q个任务。
每行首先有1个正整数k。若k=1,表示任务是传递情报,随后有3个正整数Xi、Yi、Ci,依次表示传递
情报的起点、终点和风险控制值;若k=2,表示任务是搜集情报,随后有1个正整数Ti,表示搜集情报的
情报员编号。
输出
对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开。输出不应包含多余的空行和空格。
样例输入
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
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
样例输出
5 0
5 2
5 1
5 2
5 1
提示
对于3个传递情报任务,都是经过5名情报员,分别是4号、2号、1号、3号和7号。其中,对于第1个
任务,所有情报员(危险值为0)都不对情报构成威胁;对于第2个任务,有2名情报员对情报构成威胁,
分别是1号情报员(危险值为3)和4号情报员(危险值为2),7号情报员(危险值为1)并不构成威胁;
对于第3个任务,只有1名情报员对情报构成威胁。
n< = 2×10^5,Q< = 2×105,0< Pi,C!< = N, 1< = Ti,Xi,Yi< = n
树上主席树,先把所有操作读进来,记录每个点开始调查情报时间,再dfs一遍整棵树,在每个点建一棵主席树,每个点的主席树状态由父节点转移过来。
对于第i次操作询问x,y,c,就是查询路径在主席树上的信息中开始调查情报的时间在i-c之前有几个点。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct node { int opt; int x,y; int z; }a[200010]; int n,m; int tot; int cnt; int rot; int v[200010]; int d[200010]; int to[200010]; int ls[6000010]; int rs[6000010]; int sum[6000010]; int root[1000010]; int head[200010]; int next[200010]; int f[200010][20]; void add(int x,int y) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; } void dfs(int x,int fa) { d[x]=d[fa]+1; for(int i=1;i<=19;i++) { f[x][i]=f[f[x][i-1]][i-1]; } for(int i=head[x];i;i=next[i]) { dfs(to[i],x); } } int lca(int x,int y) { if(d[x]<d[y]) { swap(x,y); } int dep=d[x]-d[y]; for(int i=0;i<=19;i++) { if((dep&(1<<i))!=0) { x=f[x][i]; } } if(x==y) { return x; } for(int i=19;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int updata(int pre,int l,int r,int k) { int rt=++cnt; if(l==r) { sum[rt]++; return rt; } ls[rt]=ls[pre]; rs[rt]=rs[pre]; sum[rt]=sum[pre]+1; int mid=(l+r)>>1; if(k<=mid) { ls[rt]=updata(ls[pre],l,mid,k); } else { rs[rt]=updata(rs[pre],mid+1,r,k); } return rt; } void build(int x,int fa) { root[x]=root[fa]; if(v[x]!=0) { root[x]=updata(root[fa],1,m,v[x]); } for(int i=head[x];i;i=next[i]) { build(to[i],x); } } int query(int x,int y,int fa,int anc,int l,int r,int k) { if(l==r) { return sum[x]+sum[y]-sum[fa]-sum[anc]; } int res=sum[ls[x]]+sum[ls[y]]-sum[ls[fa]]-sum[ls[anc]]; int mid=(l+r)>>1; if(k<=mid) { return query(ls[x],ls[y],ls[fa],ls[anc],l,mid,k); } else { return res+query(rs[x],rs[y],rs[fa],rs[anc],mid+1,r,k); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&f[i][0]); add(f[i][0],i); } for(int i=1;i<=n;i++) { if(f[i][0]==0) { rot=i; dfs(i,0); break; } } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&a[i].opt); if(a[i].opt==1) { scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); } else { scanf("%d",&a[i].z); v[a[i].z]=i; } } build(rot,0); for(int i=1;i<=m;i++) { if(a[i].opt==1) { int anc=lca(a[i].x,a[i].y); printf("%d ",d[a[i].x]+d[a[i].y]-d[anc]-d[f[anc][0]]); if(i-a[i].z-1<=0) { printf("0\n"); continue; } printf("%d\n",query(root[a[i].x],root[a[i].y],root[anc],root[f[anc][0]],1,m,i-a[i].z-1)); } } }