CF739B Alyona and a tree 题解报告(第一篇题解)

· · 题解

俗话说解每道题都是从暴力开始的嘛
刚拿到这道题时,我的第一个想法是枚举每个起点,然后向上爬,爬到多高是多高,接着我过了样例,我本来以为只是TLE,结果直接WA掉。
然而这道题的正解是:前缀和+倍增+dfs+差分
拿着每个点,先dfs跑一下深度,顺便把TA的倍增数组预处理好,然后再倍增跑一下每一个点能被控制的最远祖先,并用差分数组处理一下,最后再用dfs跑一遍把下边的值收一下(其实就相当于前缀和),就bingo了!

#include<iostream>
using namespace std;
void read(int & x)
{
    x=0;int f=1;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar();}
    while(c<='9'&&c>='0')
    {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    x*=f;
}
void print(int x)
{
    if(x<0) {putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+48);
}
int q[200005];
struct edge{
    int w,to,nxt;
}e[200005];
int head[200005],cnt;
void add(int x,int y,int z)
{
    cnt++;
    e[cnt].w=z;
    e[cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt;
}
long long dep[200005];
int doubly[21][200005],prefix[200005];
void dfs(int s)     //预处理一个倍增数组 
{
    for(int i=1;i<=20;i++)
        doubly[i][s]=doubly[i-1][doubly[i-1][s]];
    for(int i=head[s];i;i=e[i].nxt)
    {
        int to=e[i].to;
        doubly[0][to]=s;
        dep[to]=dep[s]+e[i].w;
        dfs(to);
    }
}
void find(int now)      //找到最远的祖先 
{
    int x=now;
    for(int i=20;i>=0;--i)
        if(doubly[i][x]&&dep[now]-dep[doubly[i][x]]<=q[now]) 
            x=doubly[i][x];
    if(x!=1) prefix[doubly[0][x]]--;
    if(now!=1) prefix[doubly[0][now]]++;

}
void DFS(int x)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        DFS(to);
        prefix[x]+=prefix[to];
    }
}
int main()
{
    int n;
    read(n);
    for(int i=1;i<=n;i++)
        read(q[i]);
    for(int i=2;i<=n;i++)
    {
        int k,g;
        read(k),read(g);
        add(k,i,g);
    }
    dfs(1);
    for(int i=1;i<=n;i++)       //更改差分数组 
        find(i);
    DFS(1);     //dfs一遍求前缀和 
    for(int i=1;i<=n;i++)
        print(prefix[i]),putchar(' ');
    return 0;
}