P7126 [Ynoi2008] rdCcot gxy001

我们有个 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;
}