山险 解题报告
其中 \(n,m \leq 1e5\),\(MAXY \leq 1e3\).
注意到每个 \(i\) 只有一个对应的 \(a_{i-1}\),可以发现营地及其传送关系构成了一棵树;每一次询问相当于从书上一点一直跳到根节点,并更新路径上的点的被访问次数。
其次我们注意到,题目问的是 “编号不同” 的询问次数,那么我们就可以考虑使用树剖+可持久化线段树,每一种编号的询问对应一种版本,时间复杂度\(O(nlog^2n)\)。
但是有人会说:可持久化线段树还是太吃操作了,有没有更易于实现的算法呢。于是我们就发现:由于本题所有询问路径都是一条从树上一点到根节点的路径,因此对于一条链,(在这种编号的询问中)它被访问的部分一定是从链上一点到链顶。利用这个性质,我们可以维护每一条链最后一次被访问时的操作编号和最后一个被访问到的点,我们就可以使用线段树和树剖就可以解决问题。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ls (i<<1)
#define rs (i<<1|1)
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
const int N=1e5+100;
typedef long long ll;
typedef pair<int,int> pii;
int n,m;
struct Edge{int from,to;}e[N<<1];
int num,h[N];
void add(int f,int t){e[++num].from=h[f],e[num].to=t,h[f]=num;}
int sz[N],son[N],d[N],f[N],dfn[N],top[N],dfu,lst_tim[N],lst_p[N];
void dfs0(int u,int fa){
f[u]=fa;
d[u]=d[fa]+1;
sz[u]=1;
for(int i=h[u],v;i;i=e[i].from){
v=e[i].to;
if(v==fa) continue;
dfs0(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])
son[u]=v;
}
}
void dfs1(int u,int tp){
dfn[u]=++dfu;
top[u]=tp;
if(son[u])
dfs1(son[u],tp);
for(int i=h[u];i;i=e[i].from)
if(e[i].to!=f[u] && e[i].to!=son[u])
dfs1(e[i].to,e[i].to);
}
struct Tree{int l,r,val0,val1,tag0,tag1;}t[N<<2];//val0:次数 val1:上一次更新
void push_down(int i){
if(t[i].tag0){
t[ls].val0+=t[i].tag0;
t[ls].tag0+=t[i].tag0;
t[rs].val0+=t[i].tag0;
t[rs].tag0+=t[i].tag0;
t[i].tag0=0;
}
if(t[i].tag1){
t[ls].val1=t[ls].tag1=t[rs].val1=t[rs].tag1=t[i].tag1;
t[i].tag1=0;
}
}
void build(int i,int l,int r){
t[i].l=l,t[i].r=r;
if(l==r)
return;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void update(int i,int l,int r,int x){
if(l>r) return;
if(l<=t[i].l && t[i].r<=r){
t[i].val0++,t[i].tag0++;
t[i].val1=t[i].tag1=x;
return;
}
push_down(i);
int mid=t[i].l+t[i].r>>1;
if(l<=mid) update(ls,l,r,x);
if(mid<r) update(rs,l,r,x);
}
int query0(int i,int x){
if(t[i].l==t[i].r) return t[i].val0;
push_down(i);
int mid=t[i].l+t[i].r>>1;
return x<=mid?query0(ls,x):query0(rs,x);
}
int query1(int i,int x){
if(t[i].l==t[i].r) return t[i].val1;
push_down(i);
int mid=t[i].l+t[i].r>>1;
return x<=mid?query1(ls,x):query1(rs,x);
}
pii q[N];
void climb(int x,int tim){
while(x && lst_tim[top[x]]!=tim){
update(1,dfn[top[x]],dfn[x],tim);
lst_tim[top[x]]=tim,lst_p[top[x]]=x;
x=f[top[x]];
}
if(x && query1(1,dfn[x])!=tim){
update(1,dfn[lst_p[top[x]]]+1,dfn[x],tim);
lst_tim[top[x]]=tim,lst_p[top[x]]=x;
}
}
int main()
{
freopen("adventure.in","r",stdin);
freopen("adventure.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=2,v;i<=n;i++) scanf("%d",&v),add(i,v),add(v,i);
dfs0(1,0);
dfs1(1,1);
build(1,1,n);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].second,&q[i].first);
sort(q+1,q+m+1);
for(int i=1,tim=1;i<=m;i++){
climb(q[i].second,tim);
if(q[i].first!=q[i+1].first) tim++;
}
for(int i=1;i<=n;i++)
printf("%d\n",query0(1,dfn[i]));
return 0;
}