如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY-NC-SA 4.0 (除特别声明或转载文章外)
我们有个 naive 的想法,预处理出和每个节点编号最接近的距离在 $C$ 以内的两个(一个大于 $x$,一个小于 $x$)节点,然后莫队并查集,于是你写了,过样例了,交了,WA 了。
冷静思考一下可以发现这种做法是显然错误的,我们注意到我们 $2n$ 个信息中有重复的,有用的信息没有记录下来,为了获取尽可能多的信息,我们对于编号为 $x$ 的点,记录编号最接近的两个距离在 $C$ 以内且满足 $dep_i<dep_x\lor(dep_i=dep_x\land i<x)$ 的点,记为 $l_i,r_i$。
我们可以把询问从数连通块变为数 $l_i<l\land r_i>r$ 的点数量,为什么这么做是正确的?
因为 $dep_i<dep_x$,所以每个连通块只有深度最浅的一层点可以满足 $l_i<l\land r_i>r$ 的性质;因为 $dep_i=dep_x\land i$,同层点只有一个满足 $l_i<l\land r_i>r$ 的性质。
我们注意数据范围,猜想正解复杂度为 $O(n\log^2 n+m\log n)$。
首先,预处理出上面说的这个东西可以用点分治,对于每一个分治中心,我们把点按 $dep$ 排序插入平衡树内,平衡树节点维护到分治中心的 $dis$ 最小值,查询时直接平衡树上二分就可以了。
问题转化为每个点有两个属性 $l_i,r_i$,当 $l_i<l\land r_i>r$ 时,$i$ 对询问区间 $[l,r]$ 产生 $1$ 的贡献,直接把询问离线下来用树状数组维护答案就可以了,不会的可以去做 HH 的项链。
#include<cstdio>
#include<algorithm>
#include<chrono>
#include<random>
#define int unsigned
namespace IO{
#define BUFSIZE 20000000
struct read{
char buf[BUFSIZE],*p1,*p2,c;
read():p1(buf),p2(buf){}
inline char gc(void){
return p1==p2&&(p2=buf+fread(p1=buf,1,BUFSIZE,stdin),p1==p2)?EOF:*p1++;
}
inline read& operator >>(int& x){
for(c=gc(),x=0;c!=EOF&&(c<'0'||c>'9');c=gc());
for(;c>='0'&&c<='9';c=gc())x=x*10+(c-'0');
return *this;
}
}in;
struct write{
char buf[BUFSIZE],*p1,*p2,s[50];
int tp;
write():p1(buf),p2(buf+BUFSIZE){}
~write(){flush();}
inline void flush(void){
fwrite(buf,1,p1-buf,stdout);
p1=buf;
}
inline void pc(char c){
if(p1==p2)flush();
*p1++=c;
}
inline write& operator <<(int x){
do{s[tp++]=x%10+'0',x/=10;}while(x);
while(tp)pc(s[--tp]);
return *this;
}
inline write& operator <<(char x){
pc(x);
return *this;
}
}out;
}
using IO::in;
using IO::out;
std::mt19937 rd(std::chrono::system_clock::now().time_since_epoch().count());
int n,m,C;
int to[600010],st[300010];
int q[300010],*ed,dist[300010];
int vis[300010],sz[300010],maxi[300010],ans[600010],dep[300010],ls[300010],rs[300010],root;
int size;
void findrt(int x,int fa){
sz[x]=1,maxi[x]=0;
for(int i=st[x];i!=st[x+1];++i)
if(!vis[to[i]]&&to[i]!=fa){
findrt(to[i],x),sz[x]+=sz[to[i]];
maxi[x]=std::max(maxi[x],sz[to[i]]);
}
maxi[x]=std::max(maxi[x],size-sz[x]);
if(maxi[x]<maxi[root])root=x;
}
void dfs2(int x,int fa){
*ed++=x,dist[x]=dist[fa]+1;
for(int i=st[x];i!=st[x+1];++i)
if(to[i]!=fa&&!vis[to[i]])
dfs2(to[i],x);
}
void getdep(int x,int fa){
dep[x]=dep[fa]+1;
for(int i=st[x];i!=st[x+1];++i)
if(to[i]!=fa)
getdep(to[i],x);
}
int rt,s[300010][2],v[300010],w[300010],a[300010],cnt,pri[300010];
inline void pushup(int x){a[x]=std::min(std::min(w[x],a[s[x][0]]),a[s[x][1]]);}
inline int newnode(int x,int p){
v[++cnt]=x,a[cnt]=w[cnt]=p,s[cnt][0]=s[cnt][1]=0,pri[cnt]=rd();
return cnt;
}
inline int merge(int x,int y){
if(!x||!y) return x|y;
if(pri[x]<pri[y])return s[x][1]=merge(s[x][1],y),pushup(x),x;
return s[y][0]=merge(x,s[y][0]),pushup(y),y;
}
inline void split(int p,int k,int &x,int &y){
if(!p){x=y=0;return;}
if(v[p]<=k) return x=p,split(s[p][1],k,s[p][1],y),pushup(p);
y=p,split(s[p][0],k,x,s[p][0]),pushup(p);
}
void dfs(int x){
ed=q;
vis[x]=1;dfs2(x,0);
rt=cnt=0;
std::sort(q,ed,[](int x,int y){return dep[x]==dep[y]?x<y:dep[x]<dep[y];});
for(int *p=q;p!=ed;++p){
static int e,f,d,x;
split(rt,*p,e,f);
if(C>=dist[*p]){
d=C-dist[*p];
if(a[e]<=d){
x=e;
while(1){
if(a[s[x][1]]<=d) x=s[x][1];
else if(w[x]<=d){ls[*p]=std::max(ls[*p],v[x]);break;}
else x=s[x][0];
}
}
if(a[f]<=d){
x=f;
while(1){
if(a[s[x][0]]<=d) x=s[x][0];
else if(w[x]<=d){rs[*p]=std::min(rs[*p],v[x]);break;}
else x=s[x][1];
}
}
}
rt=merge(merge(e,newnode(*p,dist[*p])),f);
}
for(int i=st[x];i!=st[x+1];++i)
if(!vis[to[i]]){
size=sz[to[i]],root=0;
findrt(to[i],0);
dfs(root);
}
}
int hd[300010],tt[300010],nt[300010];
inline void pb(int x,int y){
static int cnt=0;
tt[++cnt]=y,nt[cnt]=hd[x],hd[x]=cnt;
}
struct que{
int l,r,id;
}pro[600010];
int c[300010],uu[300010],ph[300010],ll;
signed main(){
in>>n>>m>>C;
for(int i=2;i<=n;++i)in>>uu[i],++ph[uu[i]],++ph[i];
for(int i=1;i<=n;++i)st[i]=ll,ll+=ph[i],ph[i]=0;
st[n+1]=ll;
for(int i=2;i<=n;++i)to[st[i]+ph[i]++]=uu[i],to[st[uu[i]]+ph[uu[i]]++]=i;
getdep(1,0);
maxi[0]=n+1,size=n;
findrt(1,0);
for(int i=1;i<=n;++i)rs[i]=n+1;
dist[0]=-1,a[0]=0x3f3f3f3f;
dfs(root);
for(int i=1;i<=m;++i)in>>pro[i].l>>pro[i].r,pro[i].id=i;
std::sort(pro+1,pro+m+1,[](que a,que b){return a.r<b.r;});
auto upd=[](int i,int v){for(;i<=n;i+=i&-i)c[i]+=v;};
auto query=[](int i){int ans=0;for(;i;i&=i-1)ans+=c[i];return ans;};
for(int i=1,j=1;i<=n;++i){
upd(ls[i]+1,1),upd(i+1,-1);
for(int k=hd[i];k;k=nt[k])upd(ls[::tt[k]]+1,-1),upd(::tt[k]+1,1);
pb(rs[i],i);
for(;pro[j].r==i;++j)ans[pro[j].id]=query(pro[j].l);
}
for(int i=1;i<=m;++i)out<<ans[i]<<'\n';
return 0;
}