BZOJ4448: [Scoi2015]情报传递(主席树)
BZOJ4448: [Scoi2015]情报传递##
Time Limit: 20 Sec
Memory Limit: 256 MBDescription###
奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有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
Sample Output###
5 0
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
题解:JudgeOnline/upload/201603/Solution-4448.rar
题目地址:BZOJ4448: [Scoi2015]情报传递
题目大意:
维护一颗树,支持以下操作(可以离线)
1、给一个点赋值;
2、查询一条链上比k大的点有多个
题解:
树上做主席树的套路
对于每一个节点,把它们到根路径上的所有点权建一颗权值线段树
对于一个节点 \(u\) 和他的父亲 \(fa\)
他们两个的权值线段树只改变了一个数\((u)\) 用主席树维护(套路)
对于这道题,我们只要离线把所有赋值都赋好
对于每次查询 \(L,R\)
我们找出他们的最近公共祖先 \(Fa=lca(L,R)\) 和最近公共祖先的父亲 \(Gr=fa[Fa]\)
第一问只要输出 \(dep[L[i]]+dep[R[i]]-dep[Fa]-dep[Gr]\) 就行了
第二问只要输出 \(solve(L)+solve(R)-solve(Fa)-solve(Gr)\)
具体证明可以画画图或欧拉序推出
套路啊
类似题目 Count on a tree
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=2e5+5,M=4e6;
int n,m,rt;
int res_1,res_2,res_3,res_4;
int op[N],L[N],R[N],K[N],vis[N];
int T[M],root[M],son[M][2],fa[N][19];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct edge{
int to,next;
}e[N+N];
int cnt,last[N];
void add_edge(int u,int v){
e[++cnt]=(edge){v,last[u]};last[u]=cnt;
e[++cnt]=(edge){u,last[v]};last[v]=cnt;
}
int Time,pos[N],dep[N];
void dfs(int u,int father){
pos[++Time]=u;
dep[u]=dep[father]+1;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(v==father)continue;
fa[v][0]=u;
dfs(v,u);
}
}
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
for(int i=18;i>=0;i--)
if(dep[fa[a][i]]>=dep[b])
a=fa[a][i];
if(a==b)return a;
for(int i=18;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
int Knum;
void change(int &now,int l,int r,int k){
Knum++;
T[Knum]=T[now]+1;
son[Knum][0]=son[now][0];
son[Knum][1]=son[now][1];
now=Knum;
if(l==r)return;
int mid=(l+r)>>1;
if(k<=mid)change(son[now][0],l,mid,k);
else change(son[now][1],mid+1,r,k);
}
int query(int now,int l,int r,int L,int R){
if(L>R)return 0;
if(L<=l && r<=R)return T[now];
int mid=(l+r)>>1,res=0;
if(L<=mid)res+=query(son[now][0],l,mid,L,R);
if(R>mid)res+=query(son[now][1],mid+1,r,L,R);
return res;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int fa=read();
if(fa==0)rt=i;
else add_edge(fa,i);
}
dfs(rt,0);
for(int j=1;j<=18;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
m=read();
for(int i=1;i<=m;i++){
op[i]=read();
if(op[i]==1)L[i]=read(),R[i]=read(),K[i]=read();
else{
int x=read();
vis[x]=i;
}
}
for(int i=1;i<=n;i++){
root[pos[i]]=root[fa[pos[i]][0]];
if(vis[pos[i]])
change(root[pos[i]],1,m,vis[pos[i]]);
}
for(int i=1;i<=m;i++)
if(op[i]==1){
int Fa=lca(L[i],R[i]),Gr=fa[Fa][0];
int k=i-K[i]-1;
res_1=query(root[L[i]],1,m,1,k);
res_2=query(root[R[i]],1,m,1,k);
res_3=query(root[Fa],1,m,1,k);
if(Gr)res_4=query(root[Gr],1,m,1,k);
else res_4=0;
printf("%d %d\n",dep[L[i]]+dep[R[i]]-dep[Fa]-dep[Gr],res_1+res_2-res_3-res_4);
}
return 0;
}
作者:skl_win
出处:https://www.cnblogs.com/shaokele/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。