题目描述

给 $n$ 个节点的树。定义 $g(a,b)$ 表示 $a$ 的子树中除 $b$ 之外深度不超过 $b$ 的节点个数,定义 $f(v) = \sum_{x \in anc(v)} g(x,v)$ (其中 $anc(v)$ 是 $v$ 的祖先组成的集合)。
现在要对于每一个点,求出 $f(v)$
$n \leq 10^5$

题解

如果这题 $n \leq 10^5$ 那还是很 eazy 的。。可惜。。
首先我们对 $f(v)$ 求的东西做一个转化:我们考虑每个点 $i$ 只有在 $dep_i \leq dep_v$ 的时候会被贡献到,并且会被贡献 $dep_{lca(v,i)}$ 次。
也就是说 $f(v) = \sum_{dep_x \leq dep_v} dep_{lca(v,x)}$
我们分层考虑计算答案,发现每个节点会在它到根的路径上有贡献,查询节点的答案时只需要查询到根的和就好了(这样会在 lca 处被算到应该的次数),直接使用树链剖分。
但是这是两个 log,显然过不去。。
考虑我们每层的节点都按照 dfn 排序,现在计算这个点 $v$ 的答案,考虑如果转移到 dfn 更大的一个点 $v'$ 的时候,其他的点的 $dep$ 是单调不增的(最近的一段会变成现在的 lca,再往后就是之前的 lca)。我们可以用一个单调栈轻松维护。

/*
 * Author: RainAir
 * Time: 2019-10-09 20:20:00
 */
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cstdio>
#include <bitset>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>

#define fi first
#define se second
#define U unsigned
#define P std::pair
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 5e5 + 5;
const int MAXM = 19;

struct Edge{
    int to,nxt;
}e[MAXN<<1];
int n;
int head[MAXN],f[MAXN][MAXM+1],dep[MAXN],dfn[MAXN],cnt;
std::vector<int> G[MAXN];

inline void add(int u,int v){
    e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
    e[++cnt] = (Edge){u,head[v]};head[v] = cnt;
}

inline void dfs(int v,int fa=0){
    static int ts = 0;
    dfn[v] = ++ts;
    FOR(i,1,MAXM) f[v][i] = f[f[v][i-1]][i-1];
    G[dep[v]].pb(v);
    for(int i = head[v];i;i = e[i].nxt){
        if(e[i].to == fa) continue;
        dep[e[i].to] = dep[v] + 1;
        f[e[i].to][0] = v;dfs(e[i].to,v);
    }
}

inline int lca(int x,int y){
    if(dep[x] != dep[y]){
        if(dep[x] < dep[y]) std::swap(x,y),std::swap(dep[x],dep[y]);
        int d = dep[x] - dep[y];
        FOR(i,0,MAXM) if((1<<i)&d) x = f[x][i];
    }
    if(x == y) return x;
    ROF(i,MAXM,0){
        if(f[x][i] == f[y][i]) continue;
        x = f[x][i],y = f[y][i];
    }
    return f[x][0];
}

LL ans[MAXN];
int a[MAXN],b[MAXN],c[MAXN],top;
// a: point b: dep c: id

inline void Solve(std::vector<int> &v){
    top = 0;LL res = 0;
    FOR(i,0,(int)v.size()-1){
        int x = v[i];
        if(!i) a[++top] = x,b[top] = 0,c[top] = 0;
        else{
            while("Sqytxdy"){
                int l = lca(a[top],x);
                if(b[top] <= dep[l]){
                    a[++top] = x;b[top] = dep[l];c[top] = i;
                    break;
                }
                res -= 1ll*(c[top]-c[top-1])*b[top];
                --top;
            }
            res += 1ll*(c[top]-c[top-1])*b[top];
        }
        ans[x] += res;
    }
}

int main(){
    scanf("%d",&n);int root = 0;
    FOR(i,1,n){
        int f;scanf("%d",&f);
        if(f) add(f,i);
        else root = i;
    }
    dep[root] = 1;dfs(root);
    FOR(d,1,n){
        for(auto x:G[d]) ans[x] += ans[f[x][0]] + d;
        Solve(G[d]);
        std::reverse(all(G[d]));
        Solve(G[d]);
    }
    FOR(i,1,n) printf("%lld ",ans[i]-dep[i]);puts("");
    return 0;
}

版权属于:RainAir

本文链接:https://blog.aor.sd.cn/archives/860/

转载时须注明出处及本声明

Last modification:April 7th, 2020 at 10:14 pm
如果觉得我的文章对你有用,请随意赞赏