bzoj 4448 [Scoi2015]情报传递 (树链剖分+主席树)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4448
题面:
Description
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有n名情报员。每名情报员口J-能有
若T名(可能没有)下线,除1名大头日外其余n-1名情报员有且仅有1名上线。奈特公司纪律森严,每
名情报员只能与自己的上、下线联系,同时,情报网络中仟意两名情报员一定能够通过情报网络传递情报。
奈特公司每天会派发以下两种任务中的一个任务:
1.搜集情报:指派T号情报员搜集情报
2.传递情报:将一条情报从X号情报员传递给Y号情报员
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为0;-旦某个情报员开
始搜集情报,他的危险值就会持续增加,每天增加1点危险值(开始搜集情报的当天危险值仍为0,第2天
危险值为1,第3天危险值为2,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值C。余特公司认为,参与传递这条情
报的所有情报员中,危险值大于C的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每
个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。
Input
第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,表示搜集情报的
情报员编号。
Output
对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开。输出不应包含多余的空行和空格。
Sample Input
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
Sample Output
5 0
5 2
5 1
5 2
5 1
HINT
对于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
思路:
这道题题意很好理解,就不说了,,
我们可以将k = 2的操作全部离线处理掉,对于k = 1的操作我们需要输出两个值:第一个值:两点间经过多少个点。。。这个树剖剖完后树就变成了一段区间,要求【x,y】区间的值
我们可以用【1,x】区间加上【1,y】区间减去两倍的【1,min(x,y)】区间再+1就好了,,如果看不懂可以去画下图就好了;
求第二个值我们只要在链(x,y)上找到所有 小于等于 i - ci - 1 的值就好了,这里用主席树区间查询下就好了。
写法:先用树链剖分将这棵树变成线性结构再用主席树处理下就好了。
实现代码:
#include<bits/stdc++.h> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid int m = (l + r) >> 1 const int M = 2e5+10; const int Max = 5e6+10; struct node{ int to,next; }e[M<<2]; int head[M]; int dep[M],fa[M],siz[M],son[M],top[M],tid[M],rk[M],cnt,cnt1; int ls[Max],rs[Max],sum[Max],root[Max],idx,n,q,t[M]; void add(int u,int v){ e[++cnt1].to = v; e[cnt1].next = head[u];head[u] = cnt1; } void dfs1(int u,int faz,int deep){ dep[u] = deep; fa[u] = faz; siz[u] = 1; for(int i = head[u];i;i = e[i].next){ int v = e[i].to; if(v != fa[u]){ dfs1(v,u,deep+1); siz[u] += siz[v]; if(son[u] == -1||siz[v] > siz[son[u]]) son[u] = v; } } } void dfs2(int u,int t){ top[u] = t; tid[u] = cnt; rk[cnt] = u; cnt++; if(son[u] == -1) return ; dfs2(son[u],t); for(int i = head[u];i;i = e[i].next){ int v = e[i].to; if(v != son[u]&&v != fa[u]) dfs2(v,v); } } void update(int old,int &k,int l,int r,int p){ k = ++idx; ls[k] = ls[old]; rs[k] = rs[old]; sum[k] = sum[old] + 1; if(l == r) return ; mid; if(p <= m) update(ls[old],ls[k],l,m,p); else update(rs[old],rs[k],m+1,r,p); } int query(int L,int R,int l,int r,int p){ L = root[L - 1], R = root[R]; int ret = 0; while(l != r){ mid; if(p <= m) L = ls[L] ,R = ls[R], r = m; else ret += sum[ls[R]] - sum[ls[L]],L = rs[L],R = rs[R],l = m+1; } if(p >= l) ret += sum[R] - sum[L]; return ret; } int solve(int x,int y,int p){ int ans = 0; int fx = top[x],fy = top[y]; int tmp = dep[x] + dep[y]; while(fx != fy){ if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy); ans += query(tid[fx],tid[x],1,q,p); x = fa[fx]; fx = top[x]; } if(dep[x] > dep[y]) swap(x,y); ans += query(tid[x],tid[y],1,q,p); cout<<tmp - 2*dep[x] + 1<<" "<<ans<<endl; } int x,k,w,u[M],v[M],c[M]; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); memset(son,-1,sizeof(son)); cin>>n; for(int i = 1;i <= n;i ++){ cin>>x; add(x,i); } dfs1(0,-1,1); dfs2(0,0); cin>>q; memset(t,q,sizeof(t)); int cnt2 = 0; for(int i = 1;i <= n;i ++) t[i] = q; for(int i = 1;i <= q;i ++){ cin>>k; if(k == 1){ cin>>u[cnt2]>>v[cnt2]>>w; c[cnt2++] = i - w - 1; } else cin>>x,t[x] = i; } for(int i = 1;i <= n;i ++) update(root[i-1],root[i],1,q,t[rk[i]]); for(int i = 0;i < cnt2;i ++) solve(u[i],v[i],c[i]); }