题目描述:
Alyona有一棵有
n
n
n 个节点的树。这棵树的根节点是
1
1
1。在每个节点里,Alyona写了一个正整数,在节点
i
i
i 她写了正整数
a
i
a_i
ai。另外,她在这棵树上的每条边上写了一个正整数(不同边上可能有不同的数)。
让我们定义
d
i
s
t
(
v
,
u
)
dist(v,u)
dist(v,u) 作为从
v
v
v 到
u
u
u 的简单路径上的边权和。
当且仅当
u
u
u 在
v
v
v 的子树中并且
d
i
s
t
(
v
,
u
)
≤
a
u
dist(v,u)≤a_u
dist(v,u)≤au,顶点
v
v
v 控制顶点
u
(
v
!
=
u
)
u(v!=u)
u(v!=u) 。
Alyona想在某些顶点定居。为了做到这件事,她想知道在每个节点
v
v
v 能控制几个节点。
分析:
显然对于每个节点,考虑对父亲的贡献。可以倍增到能贡献到的深度最浅的祖先,把从他的父亲到该祖先节点所有+1,可以树上差分解决。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int n,cnt;
int a[maxn],f[maxn][20],sum[maxn],ls[maxn];
LL dis[maxn][20];
struct edge{
int y,w,next;
}g[maxn];
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,ls[x]};
ls[x]=cnt;
}
void dfs(int x)
{
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y,w=g[i].w;
f[y][0]=x;
dis[y][0]=(LL)w;
dfs(y);
}
}
void calc(int x)
{
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
calc(y);
sum[x]+=sum[y];
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=2;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,i,y);
}
dfs(1);
dis[1][0]=1e15;
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
dis[i][j]=dis[i][j-1]+dis[f[i][j-1]][j-1];
}
}
int p,k;
for (int i=1;i<=n;i++)
{
p=i,k=19;
while (k>=0)
{
if (dis[p][k]<=a[i])
{
a[i]-=dis[p][k];
p=f[p][k];
}
k--;
}
sum[f[i][0]]++;
sum[f[p][0]]--;
}
calc(1);
for (int i=1;i<n;i++) printf("%d ",sum[i]);
printf("%d\n",sum[n]);
}