[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

来源:互联网 发布:淘宝云客服招聘 编辑:程序博客网 时间:2024/05/17 02:30

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分。其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过。

我们先来把树上差分能做到的看一下:

1.找所有路径公共覆盖的边

例题:[NOIP2015]运输计划 (然而我还没过就先不讲了)

反正就是中间有一步要求一条边被所有计划公共覆盖。

那么怎么求它呢?暴力(滚粗)。我们有一个非常好的方法就是树上差分(记录tmp为差分数组)

询问操作为从叶子节点的权值向上累加到root

在一条路径u→ v,如果tmp[u]++,那么我们往上推的时候相当于u到root所有路径都被访问一次。同理tmp[v]++也意味如此。但是,lca(u,v)到root的路径都没有被访问过,但这里都被标记过两次,所以我们还要做的操作就是tmp[lca(u,v)]-=2;这样的话累加完之后tmp[i]记录的就是i节点被多少条路径覆盖了。

2.将路径上的所有点权值+1,最后求点权

例题:[JLOI2014]松鼠的新家 (这个我做过了hhh)

题目大意就是给你一些路径,把这个路径经过的点权+1,最后求所有点权。

这个题今天卡了我了。同学大佬有拿树剖求的,而且还要差分。但是我对于树上差分有点蒙蔽,于是搜了搜。然而蒟蒻的我搜到了LCA解法,于是兴高采烈的打(chao)了(le)出来。这里的差分有一些不同。因为我们要找的是点的覆盖。所以我们对于u→ v,tmp[u]++,tmp[v]++,tmp[lca(u,v)]--,tmp[fa[lca(u,v)]]--;这个想必大家能看懂吧。

于是,我们就欢快的求出了所有点被修改后的权值。

#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>#include<queue>#include<cmath>#define pos(i,a,b) for(int i=(a);i<=(b);i++)using namespace std;#define N 301000struct haha{    int next,to;}edge[N*2];int head[N],cnt=1,p[N][20];void add(int u,int v){    edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}int a[N],n,fa[N],dep[N];void dfs(int x){    for(int i=head[x];i;i=edge[i].next){        int to=edge[i].to;        if(fa[x]!=to){            fa[to]=x;dep[to]=dep[x]+1;            dfs(to);        }    }}void init(){    int j;    for(j=0;(1<<j)<=n;j++){        pos(i,1,n) p[i][j]=-1;    }    pos(i,1,n){        p[i][0]=fa[i];    }    for(j=1;(1<<j)<=n;j++){        pos(i,1,n){            if(p[i][j-1]!=-1){                p[i][j]=p[p[i][j-1]][j-1];            }        }    }}int lca(int a,int b){    int i;    if(dep[a]<dep[b]) swap(a,b);    for(i=0;(1<<i)<=dep[a];i++);    i--;    for(int j=i;j>=0;j--)        if(dep[a]-(1<<j)>=dep[b])            a=p[a][j];    if(a==b) return a;    for(int j=i;j>=0;j--){        if(p[a][j]!=-1&&p[a][j]!=p[b][j]){            a=p[a][j];b=p[b][j];        }    }    return fa[a];}int tmp[N];void work(int x){    for(int i=head[x];i;i=edge[i].next){        int to=edge[i].to;        if(fa[x]!=to){            work(to);            tmp[x]+=tmp[to];        }    }}int main(){    scanf("%d",&n);    pos(i,1,n)        scanf("%d",&a[i]);    pos(i,1,n-1){        int x,y;scanf("%d%d",&x,&y);        add(x,y);add(y,x);    }    dfs(a[1]);    init();    pos(i,1,n-1){        int u=a[i],v=a[i+1];        tmp[u]++;tmp[v]++;        tmp[lca(u,v)]--;tmp[fa[lca(u,v)]]--;    }    work(a[1]);    pos(i,2,n) tmp[a[i]]--;    pos(i,1,n) printf("%d\n",tmp[i]);    return 0;}


原创粉丝点击