CF739B Alyona and a tree

CF739B Alyona and a tree

洛谷传送门

题目描述:

Alyona有一棵有 nn 个节点的树。这棵树的根节点是 11。在每个节点里,Alyona写了一个正整数,在节点 ii 她写了正整数 a_ia**i 。另外,她在这棵树上的每条边上写了一个正整数(不同边上可能有不同的数)。

让我们定义 dist(v,u)dis**t(v,u) 作为从 vv 到 uu 的简单路径上的边权和。

当且仅当 uu 在 vv 的子树中并且 dist(v,u)\leq a_udis**t(v,u)≤a**u,顶点 vv 控制顶点 u(v!=u)u(v!=u) 。

Alyona想在某些顶点定居。为了做到这件事,她想知道在每个节点 vv 能控制几个节点。

输入格式:

第一行包含一个整数 n (1\leq n\leq 2\times 10^5)n(1≤n≤2×105)

第二行有 nn 个整数 a_1,a_2,\ldots,a_n(1\leq a_i\leq 10^9)a1,a2,…,a**n(1≤a**i≤109) ,作为节点 ii 的数。

下面的 n-1n−1 行,每行有两个整数。第 ii 行包含整数 p_i,w_i(1\leq p_i\leq n,1\leq w_i\leq 10^9)p**i,w**i(1≤p**in,1≤w**i≤109) ,分别为节点 i+1i+1 的在树上的父节点和 p_ip**i 和 (i+1)(i+1) 的边上的数字。

数据保证是个树。

输出格式:

输出 nn 个整数,第 ii 个数为节点 ii 能控制的点数。

样例说明:

在样例中,节点 11 控制了节点 33 ,节点 33 控制节点 55 (注意,这并不代表节点 11 控制了节点 55 )

Translated by @lolte


题解:

不是很难理解题意。一开始想到的是维护距离前缀和,然后对每个点进行深搜,这样的话复杂度是\(O(n^2)\)的。然后想到可以反向维护,一个节点只可能对它所有的祖先节点产生贡献,这样的话复杂度是最优\(O(n\log n)\),最坏\(O(n^2)\)的,还是会被卡。

然后思路断了,但是还是我太菜了,本来想到这,枚举祖先,一定会想到倍增优化枚举,但是本蒟蒻就是没想到。所以严格\(O(n\log n)\)的思路就出来了:树上倍增维护一个点对祖先的贡献,这里可以用一遍DFS来处理,然后用树上差分统计答案。记得开Longlong。

代码:

#include<cstdio>
#define int long long
using namespace std;
const int maxn=2e5+5;
int n;
int a[maxn];
int tot,head[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1];
int fa[maxn][22],dist[maxn],w[maxn],ans[maxn];
//fa[x][k]表示x的2^k辈祖先是谁。
void add(int x,int y,int z)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    val[tot]=z;
    head[x]=tot;
}
void dfs1(int x,int f)
{
    fa[x][0]=f;
    for(int i=1;i<=21;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        w[y]=val[i];
        dist[y]=dist[x]+w[y];
        dfs1(y,x);
    }
}
void dfs2(int x)
{
    int pos=x;
    for(int i=21;i>=0;i--)
        if(fa[pos][i] && dist[x]-dist[fa[pos][i]]<=a[x])
            pos=fa[pos][i];
    ans[fa[pos][0]]--;
    ans[fa[x][0]]++;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==fa[x][0])
            continue;
        dfs2(y);
        ans[x]+=ans[y];
    }
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=2;i<=n;i++)
    {
        int f,ww;
        scanf("%lld%lld",&f,&ww);
        add(f,i,ww);
        add(i,f,ww);
    }
    dfs1(1,0);
    dfs2(1);
    for(int i=1;i<=n;i++)
        printf("%lld ",ans[i]);
    return 0;
}
posted @ 2020-12-01 11:08  Seaway-Fu  阅读(80)  评论(0编辑  收藏  举报