zoukankan      html  css  js  c++  java
  • 学习笔记--树上差分

    • 前言

      在做一些树上路径修改&查询相关题目时,有时我们用不着树链剖分,类比于序列上的差分,我们可以进行树上差分,不过情况稍有些不同,分为点值上的差分和边权上的差分两种

    • 点值差分

      对树上路径(path(x,y))进行点值差分方法:

      (tag[x]++,tag[y]++,tag[lca(x,y)]-=2)

      询问(x)被多少个标记覆盖时进行(dfs),将(x)所有子树节点(tag[])之和加上(tag[x])即使被覆盖数目

      例题:https://www.luogu.org/problemnew/show/P3128

      代码:

    include

    include

    include

    include

    include

    include

    include

    include

    include

    define ll long long

    define ri register int

    using namespace std;
    const int maxn=50005;
    const int inf=0x7fffffff;
    template inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c'-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;
    return ;
    }
    int n,k;
    struct Edge{
    int ne,to;
    }edge[maxn<<1];
    int h[maxn],num_edge=0;
    inline void add_edge(int f,int to){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    h[f]=num_edge;
    return ;
    }
    int cnt=0;
    int dep[maxn],fa[maxn],son[maxn],top[maxn],dfn[maxn],rnk[maxn],size[maxn];
    int sum[maxn];
    int L,R,dta;
    void dfs_1(int now){
    int v;
    size[now]=1;
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(v
    fa[now])continue;
    dep[v]=dep[now]+1,fa[v]=now;
    dfs_1(v);
    size[now]+=size[v];
    if(!son[now]||size[son[now]]<size[v])son[now]=v;
    }
    return ;
    }
    void dfs_2(int now,int t){
    int v;
    top[now]=t,dfn[now]=++cnt,rnk[cnt]=now;
    if(!son[now])return ;
    dfs_2(son[now],t);
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(vfa[now]||vson[now])continue;
    dfs_2(v,v);
    }
    return ;
    }
    void update_lca(int x,int y){
    while(top[x]!=top[y]){
    if(dep[top[x]]<dep[top[y]])swap(x,y);
    x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    sum[x]--,sum[fa[x]]--;
    return ;
    }
    int ans=-inf;
    void dfs_3(int now){
    int v;
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(v==fa[now])continue;
    dfs_3(v);
    sum[now]+=sum[v];
    }
    ans=max(ans,sum[now]);
    return ;
    }
    int main(){
    int x,y,z;
    //double st=clock();
    read(n),read(k);
    for(ri i=1;i<n;i++){
    read(x),read(y);
    add_edge(x,y);
    add_edge(y,x);
    }
    dep[1]=1,fa[1]=0;
    dfs_1(1);
    dfs_2(1,1);
    for(ri i=1;i<=k;i++){
    read(x),read(y);
    sum[x]++,sum[y]++;
    update_lca(x,y);
    }
    //double ed=clock();
    dfs_3(1);
    printf("%d ",ans);
    //printf("%lf ",ed-st);
    return 0;
    }

    - 边权差分
    
    对树上路径$(x,y)$进行差分方法:(注意$x,y$这里还是节点)
    
    $tag[x]++,tag[y]++,tag[lca(x,y)]--,tag[fa[lca(x,y)]]--$
    
    询问$x$被多少标记覆盖方法同上,然而**注意**!!
    
    解决相关问题时不能把$tag[root]$算进贡献,因为它没有后继的边
    
    例题:http://poj.org/problem?id=3417
    
    代码:
    
    

    include

    include

    include

    include

    include

    include

    include

    include

    include

    define ll long long

    define ri register int

    using namespace std;
    const int maxn=100005;
    const int inf=0x7fffffff;
    template inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c'-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;
    return ;
    }
    struct Edge{
    int ne,to;
    }edge[maxn<<1];
    int h[maxn],num_edge=0,n,m;
    inline void add_edge(int f,int t){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=t;
    h[f]=num_edge;
    return ;
    }
    int dep[maxn],fa[maxn],size[maxn],dfn[maxn],sum[maxn],son[maxn],top[maxn];
    void dfs_1(int now){
    int v;
    size[now]=1;
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(v
    fa[now])continue;
    fa[v]=now,dep[v]=dep[now]+1;
    dfs_1(v);
    size[now]+=size[v];
    if(!son[now]||size[son[now]]<size[v])son[now]=v;
    }
    return ;
    }
    void dfs_2(int now,int t){
    int v;
    top[now]=t;
    if(!son[now])return ;
    dfs_2(son[now],t);
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(vfa[now]||vson[now])continue;
    dfs_2(v,v);
    }
    }
    int ans;
    void dfs_3(int now){
    int v;
    for(ri i=h[now];i;i=edge[i].ne){
    v=edge[i].to;
    if(vfa[now])continue;
    dfs_3(v);
    sum[now]+=sum[v];
    }
    //cout<<sum[now]<<endl;
    if(now!=1&&sum[now]
    0)ans+=m;
    else if(now!=1&&sum[now]==1)ans++;
    return ;
    }
    void update_path(int x,int y){
    while(top[x]!=top[y]){
    if(dep[top[x]]<dep[top[y]])swap(x,y);
    x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    sum[x]-=2;
    return;
    }
    int main(){
    int x,y,z;
    read(n),read(m);
    for(ri i=1;i<n;i++){
    read(x),read(y);
    add_edge(x,y);
    add_edge(y,x);
    }
    fa[1]=0,dep[1]=1;
    dfs_1(1);
    dfs_2(1,1);
    for(ri i=1;i<=m;i++){
    read(x),read(y);
    sum[x]++,sum[y]++;
    update_path(x,y);
    }
    dfs_3(1);
    printf("%d ",ans);
    return 0;
    }

    
    - 例题待填坑
    
    - 货车运输
    
    - 天天爱跑步
  • 相关阅读:
    【NOIP2007】守望者的逃离
    20200321(ABC)题解 by 马鸿儒 孙晨曦
    20200320(ABC)题解 by 王一帆
    20200319(ABC)题解 by 王一帆 梁延杰 丁智辰
    20200314(ABC)题解 by 董国梁 蒋丽君 章思航
    20200309(ABC)题解 by 梁延杰
    20200307(DEF)题解 by 孙晨曦
    20200306(ABC)题解 by 孙晨曦
    20200305(DEF)题解 by 孙晨曦
    20200303(ABC)题解 by 王锐,董国梁
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9271382.html
Copyright © 2011-2022 走看看