BZOJ4448: [Scoi2015]情报传递
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
题解Here!
额,其实还是蛮好想的。
树上问题就直接套一个树链剖分嘛。
题目要求:链上所有威胁值大于$x$的点的个数。
转化一下,就是:链上开始搜集的日子小于$i-x-1$的点的个数,$i$为当前的时间。
显然主席树啊!
然后就变成了树剖套主席树。
复杂度$O(n\log_2^2n)$。
但是!$n\leq 2\times 10^5$!
这。。。好像会$TLE$吧。。。
哦,时限$2s$。。。
但是我们还有更快的算法!
树上主席树!
简单的来说就是如果我们要查询$(u,v)$,那么我们实际查询树上前缀和(假设前缀和数组为$sum[i]$)的就是:
$$sum[u]+sum[v]-sum[LCA(u,v)]-sum[fa[LCA(u,v)]]$$
$fa[i]$表示$i$的父亲。
然后把那个前缀和换成主席树即可。
复杂度$O(n\log_2n)$。
于是我们的树剖出了求$LCA$之外好像没有啥作用。。。
注:然后我用实践证明了什么是代码$10min$,调试一下午。。。
我竟然把从来没有敲错的树链剖分板子敲错了。。。
然后。。。可想而知。。。$TLE$了一下午。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 200010 using namespace std; int n,m,c=1; int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],top[MAXN]; int val[MAXN],root[MAXN]; struct Tree{ int next,to; }a[MAXN]; struct Question{ int u,v,w; }que[MAXN]; inline int read(){ int date=0;char c=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date; } namespace CT{ int size=0; struct Chairmen_Tree{ int sum,l,r; }a[MAXN*20]; inline void buildtree(){ root[0]=a[0].l=a[0].r=a[0].sum=0; } void insert(int k,int l,int r,int &rt){ a[++size]=a[rt];rt=size; a[rt].sum++; if(l==r)return; int mid=l+r>>1; if(k<=mid)insert(k,l,mid,a[rt].l); else insert(k,mid+1,r,a[rt].r); } int query(int u,int v,int fa,int fafa,int l,int r,int lside,int rside){ int ans=0; if(l<=lside&&rside<=r)return (a[u].sum+a[v].sum-a[fa].sum-a[fafa].sum); int mid=lside+rside>>1; if(l<=mid)ans+=query(a[u].l,a[v].l,a[fa].l,a[fafa].l,l,r,lside,mid); if(mid<r)ans+=query(a[u].r,a[v].r,a[fa].r,a[fafa].r,l,r,mid+1,rside); return ans; } } inline void add(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; } void dfs1(int rt){ son[rt]=0;size[rt]=1; root[rt]=root[fa[rt]]; if(val[rt])CT::insert(val[rt],1,m,root[rt]); for(int i=head[rt],will;i;i=a[i].next){ will=a[i].to; deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will];//就是这里!当初我把两个弄反了。。。然后就。。。 if(size[son[rt]]<size[will])son[rt]=will; } } void dfs2(int rt,int f){ top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(will!=fa[rt]&&will!=son[rt])dfs2(will,will); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); return x; } void work(){ int u,v,w,lca; for(int i=1;i<=m;i++)if(que[i].u){ u=que[i].u;v=que[i].v;w=que[i].w; lca=LCA(u,v); w=i-w-1; printf("%d ",deep[u]+deep[v]-2*deep[lca]+1); if(w<1)printf("0\n"); else printf("%d\n",CT::query(root[u],root[v],root[lca],root[fa[lca]],1,w,1,m)); } } void init(){ int f,u,root; n=read(); CT::buildtree(); for(int i=1;i<=n;i++){ u=read(); if(u)add(u,i); else root=i; val[i]=0; } m=read(); for(int i=1;i<=m;i++){ f=read();que[i].u=0; if(f==1){ que[i].u=read();que[i].v=read();que[i].w=read(); } else{ u=read(); val[u]=i; } } deep[root]=1; dfs1(root); dfs2(root,root); } int main(){ init(); work(); return 0; }