• 学习笔记--树上差分


    • 前言

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

    • 点值差分

      对树上路径(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;
    }

    
    - 例题待填坑
    
    - 货车运输
    
    - 天天爱跑步
  • 相关阅读:
    单例模式
    maven版本对应的jdk
    DateUtil
    多级反向代理java获取真实IP地址
    springcloud初次zuul超时报错com.netflix.zuul.exception.ZuulException:Forwarding error
    spring cloud-config的client中/refresh的端点报错401
    spring Cloud-eureka的保护模式
    spring cloud的配置
    spring boot部署中executable的系统服务
    登陆SQL Server 2000数据库提示超时已过期的解决方法
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9271382.html
Copyright © 2020-2023  润新知