【BZOJ4448】【SCOI2015】情报传递
这题面错别字真tm多
题意:
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,示搜集情报的情报员编号。
n< = 2×10^5,Q< = 2×10^5,0< Pi,C< = N, 1< = Ti,Xi,Yi< = n
Output
对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开。输出不应包含多余的空行和空格。
题解:
数据结构学傻了->树剖随便做!
其实树剖的确可以做。。。虽然多了一个$log$但是数据范围小+常数小所以能过。。。
正解是树上主席树(新套路get)
先离线,然后危险值大于$C_i$的点的个数就是在$C_i-i$的时间之前开始收集情报的点的个数;
然后可以建一颗权值主席树,维护某段时间内的点个数。
每个点$x$在$fa[x]$上建主席树,然后查询的时候答案就是
$sum[x]+sum[y]-sum[lca(x,y)]-sum[fa[lca(x,y)]]$
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
using namespace std;
typedef long long ll;
struct edge{
int v,next;
}a[200001];
struct node{
int v,ls,rs;
}t[5000001];
struct task{
int x,y,c,id;
}p[200001];
int n,m,f,rt,op,x,tot=0,cnt=0,tim=0,pts=0,head[200001],rts[200001],num[200001],dfn[200001],nmd[200001],dep[200001],fa[200001][20];
void add(int u,int v){
a[++tot].v=v;
a[tot].next=head[u];
head[u]=tot;
}
void dfs(int u,int dpt){
dfn[u]=++tim;
nmd[tim]=u;
dep[u]=dpt;
for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
int v=a[tmp].v;
dfs(v,dpt+1);
}
}
int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int l=dep[u]-dep[v];
for(int i=19;i>=0;i--){
if((1<<i)&l){
u=fa[u][i];
}
}
if(u==v)return u;
for(int i=19;i>=0;i--){
if(fa[u][0]!=fa[v][0]){
u=fa[u][0],v=fa[v][0];
}
}
return fa[u][0];
}
void updata(int l,int r,int &u,int k,int x){
u=++cnt;
t[u].ls=t[k].ls;
t[u].rs=t[k].rs;
t[u].v=t[k].v+1;
if(l==r)return;
int mid=(l+r)/2;
if(x<=mid)updata(l,mid,t[u].ls,t[k].ls,x);
else updata(mid+1,r,t[u].rs,t[k].rs,x);
}
int query(int l,int r,int L,int R,int a,int b,int c,int d){
if(L>R)return 0;
if(L<=l&&r<=R){
return t[a].v+t[b].v-t[c].v-t[d].v;
}
int mid=(l+r)/2,ret=0;
if(L<=mid)ret+=query(l,mid,L,R,t[a].ls,t[b].ls,t[c].ls,t[d].ls);
if(mid<R)ret+=query(mid+1,r,L,R,t[a].rs,t[b].rs,t[c].rs,t[d].rs);
return ret;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&f);
if(!f)rt=i;
else add(f,i),fa[i][0]=f;
}
dfs(rt,1);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&op);
if(op==2){
scanf("%d",&x);
num[x]=i;
}else{
pts++;
scanf("%d%d%d",&p[pts].x,&p[pts].y,&p[pts].c);
p[pts].id=i;
}
}
for(int i=1;i<=n;i++){
updata(0,m,rts[i],rts[dfn[fa[nmd[i]][0]]],num[nmd[i]]);
}
for(int i=1;i<=pts;i++){
int z=lca(p[i].x,p[i].y);
printf("%d %d\n",dep[p[i].x]+dep[p[i].y]-2*dep[z]+1,query(0,m,1,p[i].id-p[i].c-1,rts[dfn[p[i].x]],rts[dfn[p[i].y]],rts[dfn[z]],rts[dfn[fa[z][0]]]));
}
return 0;
}