[luogu3258] [JLOI2014]松鼠的新家

传送门

这道题可以用树剖或树上差分+LCA做,思路一致。每次把两个点之间的路径+1即可,但是这里是经过一次+1次,终点除外。

这里采用树上差分+LCA,LCA使用树剖维护

树上差分的思路是:每个点的权值为其子树的权值和。

修改u到v的路径,那么\(val[u]+1,val[v]+1,val[lca]-1,val[fa[lca]]-1\)

为什么是对的呢?对于这条路径,路径下的点的权值显然不会被影响,因为权值只跟子树有关。路径上的点也不会,因为两个点+1,两个点-1,没有影响。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 300005

struct edge {
    int v,next;
}G[MAXN<<1];
int head[MAXN],tot = 0;

int dfn[MAXN],top[MAXN];
int size[MAXN],son[MAXN],d[MAXN],fa[MAXN];
int num = 0;

int val[MAXN],ans[MAXN];
int a[MAXN];
int N;

inline void add(int u,int v) {
    G[++tot].v = v;G[tot].next = head[u];head[u] = tot;
}

void dfs1(int u,int father) {
    d[u] = d[father] + 1; fa[u] = father;
    size[u] = 1; son[u] = 0;
    for(int i=head[u];i;i=G[i].next) {
        int v = G[i].v;
        if(v==father) continue;
        dfs1(v,u);
        size[u] += size[v];
        if(size[son[u]]<size[v]) son[u] = v;
    }
}

void dfs2(int u,int tp) {
    top[u] = tp;dfn[u] = ++num;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=G[i].next) {
        int v = G[i].v;
        if(v==son[u]||v==fa[u]) continue;
        dfs2(v,v);
    }
}

void dfs3(int u,int father) {
    ans[u] = val[u];
    for(int i=head[u];i;i=G[i].next) {
        int v = G[i].v;
        if(v==father) continue;
        dfs3(v,u);
        ans[u] += ans[v];
    }
}

int LCA(int u,int v) {
    while(top[u]!=top[v]) {
        if(d[top[u]]<d[top[v]]) std::swap(u,v);
        u = fa[top[u]];
    }
    return d[u]<d[v] ? u : v;
}

void update(int u,int v,int w) {
    val[u] += w;val[v] += w;
    int lca = LCA(u,v);
    val[lca] -= w;
    if(fa[lca]) val[fa[lca]] -= w;
}

int main() {

    std::memset(val,0,sizeof(val));

    int u,v;
    scanf("%d",&N);
    for(int i=1;i<=N;++i) scanf("%d",&a[i]);

    for(int i=1;i<N;++i) {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }

    d[0] = size[0] = 0;
    dfs1(1,0);dfs2(1,0);

    int now = a[1];
    update(now,now,1);//计算开始节点
    for(int i=2;i<=N;++i) {
        int v = a[i];
        update(now,v,1);//修改路径
        update(now,now,-1);//起点重复计算,减去
        now = v;
    }
    update(now,now,-1);//终点不能算
    dfs3(1,0);
    for(int i=1;i<=N;++i) printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/Neworld2002/p/10009897.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值