其他分享
首页 > 其他分享> > bzoj3631: [JLOI2014]松鼠的新家(树上差分)

bzoj3631: [JLOI2014]松鼠的新家(树上差分)

作者:互联网

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3631

题目大意:给定含有n个顶点的树,给定走遍整棵树顺序的序列a[1],a[2],a[3]……a[n],表示要从a[1]走到a[2],再从a[2]走到a[3],直到走到a[n],经过的点的点权需要加1,最后输出每个点的点权。

解题思路:首先我们讨论对于走一次的情况,假设从s走到t,我们可以让ans[s]++,ans[t]++,ans[LCA(s,t)]--,并且让s和t的LCA的父亲也减1,最后上推,然后第2个节点到第n个节点多走了一次,就减去1就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=300000+7;
int n,tot,a[maxn],head[maxn],fa[maxn][25],depth[maxn],ans[maxn];
struct Edge{
    int v,next;
}edge[maxn*2];
void add(int u,int v){
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u,int pre){
    depth[u]=depth[pre]+1;
    fa[u][0]=pre;
    for(int i=1;i<=20;i++)
    fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==pre) continue;
        dfs(v,u);
    }
}
int LCA(int u,int v){
    if(depth[u]<depth[v]) swap(u,v);
    for(int i=20;i>=0;i--){
        if(depth[u]-(1<<i)>=depth[v]) u=fa[u][i];
    }
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    }
    return fa[u][0];
}
void dfs1(int u,int pre){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==pre) continue;
        dfs1(v,u);
        ans[u]+=ans[v];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    depth[0]=-1;
    dfs(1,0);
    for(int i=1;i<n;i++){
        ans[a[i]]++;
        ans[a[i+1]]++; 
        int lca=LCA(a[i],a[i+1]);
        ans[fa[lca][0]]--;
        ans[lca]--;
    }
    dfs1(1,0);
    for(int i=2;i<=n;i++)ans[a[i]]--;
    for(int i=1;i<=n;i++)
    printf("%d\n",ans[i]);
    return 0;
}

 

标签:pre,JLOI2014,int,差分,bzoj3631,fa,edge,maxn,ans
来源: https://www.cnblogs.com/zjl192628928/p/11277482.html