GDFZOJ Alyona和一棵树(dfs + LCA + 树剖或树上差分)

题目链接

题目大意

给出一棵 n n n 个点的有根树,点和边都带权 ,该树以 1 1 1 号节点为跟
d i s t ( x , y ) dist(x,y) dist(x,y) x x x y y y 的路径上的边权和
如果 u u u v v v 的子树中,且 d i s t ( u , v ) ≤ a u dist(u,v) \le a_u dist(u,v)au a a a 为点权),那么我们称顶点 v v v 控制顶点 u u u ( v ≠ u ) (v \neq u) (v=u)
对于每个节点 1 ≤ x ≤ n 1 \le x \le n 1xn,你需要求出它控制了的节点个数

解题思路

path ⁡ ( i , j ) \operatorname{path}(i,j) path(i,j) i i i j j j 的路径上的点的集合
考虑计算每个节点做出的贡献,也就是看它被哪些节点控制

因为一个点只能控制自己子树中的点,所以点 x x x 只可能对 path ⁡ ( f [ x ] , 1 ) \operatorname{path}(f[x],1) path(f[x],1) 上的点做出贡献。又因为该路径上的点到 x x x 的距离是单调递增的,所以一定存在点 y y y,满足 x x x path ⁡ ( f [ x ] , y ) \operatorname{path}(f[x],y) path(f[x],y) 上的点做出了贡献。当然,它也可能不对任何点做出贡献

我们考虑 dfs,在递归的时候维护出每个点 x x x 到根的路径上的点。因为他们离点 x x x 的距离满足单调性,所以我们可以用二分找到满足要求的最远的点 y y y,并将 path ⁡ ( f [ x ] , y ) \operatorname{path}(f[x],y) path(f[x],y) 上的点加上一点贡献(可以用树剖或树上差分实现)。

#include // 这里写的是树剖
#include
#include
#include
using namespace std;
const long long Maxn=200000+20,inf=(1ll<<60);
const long long Maxm=Maxn<<1;
long long id[Maxn],son[Maxn],top[Maxn];
long long f[Maxn][23],dis[Maxn];
long long s[Maxn],d[Maxn];
long long sum[Maxm<<1],add[Maxm<<1];
long long head[Maxn],nxt[Maxm];
long long to[Maxm],len[Maxm];
long long c[Maxn],a[Maxn],tot;
long long n,idcnt,edgecnt=1;
inline void addedge(long long x,long long y,long long val)
{
	++edgecnt;
	nxt[edgecnt]=head[x];
	to[edgecnt]=y;
	len[edgecnt]=val;
	head[x]=edgecnt;
}
inline long long read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
inline void push_up(long long k)
{
	sum[k]=sum[k<<1]+sum[k<<1|1];
}
inline void upd(long long k,long long l,long long r,long long v)
{
	add[k]+=v,sum[k]+=(r-l+1)*v;
}
inline void push_down(long long k,long long l,long long r)
{
	if(!add[k])return;
	int mid=(l+r)>>1;
	upd(k<<1,l,mid,add[k]);
	upd(k<<1|1,mid+1,r,add[k]);
	add[k]=0;
}
void modify(long long k,long long l,long long r,long long x,long long y,long long v)
{
	if(x<=l && r<=y)return upd(k,l,r,v);
	push_down(k,l,r);
	long long mid=(l+r)>>1;
	if(x<=mid)modify(k<<1,l,mid,x,y,v);
	if(mid<y)modify(k<<1|1,mid+1,r,x,y,v);
	push_up(k);
}
long long query(long long k,long long l,long long r,long long pos)
{
	if(l==r)return sum[k];
	push_down(k,l,r);
	long long mid=(l+r)>>1;
	if(pos<=mid)return query(k<<1,l,mid,pos);
	else return query(k<<1|1,mid+1,r,pos);
}
void dfs1(long long x,long long fa,long long pre)
{
	s[x]=1,f[x][0]=fa;
	d[x]=d[fa]+1,dis[x]=dis[fa]+pre;
	for(long long i=head[x];i;i=nxt[i])
	{
		long long y=to[i];
		if(y==fa)continue;
		dfs1(y,x,len[i]);
		s[x]+=s[y];
		if(s[y]>s[son[x]])son[x]=y;
	}
}
void dfs2(long long x,long long topf)
{
	id[x]=++idcnt,top[x]=topf;
	if(!son[x])return;
	dfs2(son[x],topf);
	for(long long i=head[x];i;i=nxt[i])
	{
		long long y=to[i];
		if(y==f[x][0] || y==son[x])continue;
		dfs2(y,y);
	}
}
void modify_seq(long long x,long long y)
{
	while(top[x]!=top[y])
	{
		if(d[top[x]]<d[top[y]])swap(x,y);
		modify(1,1,n,id[top[x]],id[x],1);
		x=f[top[x]][0];
	}
	if(d[x]>d[y])swap(x,y);
	modify(1,1,n,id[x],id[y],1);
}
void dfs(long long x,long long fa)
{
//	printf("dfs %lld\n",x);
	c[++tot]=x;
	long long l=1,r=tot-1;
	if(x==1 || dis[x]-dis[fa]>a[x])goto GG;
	while(l<r)
	{
		long long mid=(l+r)>>1;
		if(dis[x]-dis[c[mid]]<=a[x])r=mid;
		else l=mid+1;
	}
//	printf("check %lld %lld\n",c[l],c[tot-1]);
	modify_seq(c[l],c[tot-1]);
	GG:
	for(long long i=head[x];i;i=nxt[i])
	{
		long long y=to[i];
		if(y==fa)continue;
		dfs(y,x);
	}
	--tot;
}
int main()
{
//	freopen("in.txt","r",stdin);
	n=read();
	for(long long i=1;i<=n;++i)
	a[i]=read();
	for(long long i=2;i<=n;++i)
	{
		long long x=read(),c=read();
		addedge(x,i,c),addedge(i,x,c);
	}
	dfs1(1,0,0);
	dfs2(1,1);
	dfs(1,0);
	for(long long i=1;i<=n;++i)
	{
		long long tmp=query(1,1,n,id[i]);
		printf("%lld\n",tmp);
	}
	return 0;
}

你可能感兴趣的:(题解,GDFZOJ)