树上差分

这篇博客深入探讨了树状数组(也称作线段树)和最近公共祖先(LCA)在数据结构中的使用。通过边权差分和点权差分的概念,阐述了如何在路径上修改权重并保持子树和的正确性。文章展示了如何利用这些数据结构解决动态更新和查询问题,并给出了详细的C++代码实现,包括路径上节点权重的修改和边权重的转换。此外,还介绍了深度优先搜索(DFS)在构建LCA树中的作用以及如何进行子树差分和计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

边权差分

     将边权转化成子节点的点权,在a到b路径上边权加c等于在a和b上分别加c,在lca(a,b)上减2c,此时边权和等于子树差分和

 点权差分

     在a到b路径上点权加c等于在a和b上分别加c,在lca(a,b)和fa[lca(a,b)]上减c,此时点权和等于子树差分和

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct node
{
     int l,r,mx,sum;
}tree[100*N];
int col[N];
int h[N],nex[2*N],to[2*N],con=1,id=1,n,rt[N],par[N][20],d[N];
int ans[N],lg[N];
void add(int a,int b)
{
     nex[con]=h[a];
     h[a]=con;
     to[con++]=b;
}
void up(int now,int ls,int rs)
{
     if(tree[ls].mx==tree[rs].mx)
     {
         tree[now].mx=tree[ls].mx;
          tree[now].sum=min(tree[ls].sum,tree[rs].sum);
     }
     else if(tree[ls].mx>tree[rs].mx)
     {
          tree[now].mx=tree[ls].mx;
          tree[now].sum=tree[ls].sum;
     }
     else
     {
          
          tree[now].mx=tree[rs].mx;
          tree[now].sum=tree[rs].sum;
     }
}
void change(int l,int r,int &now,int x,int v)
{
     if(!now)
     {
          now=id++;
       //   tree[now].l=tree[now].r=tree[now].mx=tree[now].sum=0;
     }
     if(l==r)
     {
          tree[now].mx+=v;
          tree[now].sum=l;
          return;
     }
     int mid=(l+r)>>1;
     if(x<=mid)
          change(l,mid,tree[now].l,x,v);
     else
          change(mid+1,r,tree[now].r,x,v);
     up(now,tree[now].l,tree[now].r);
}
void add(int l,int r,int &a,int b)
{
    // cout<<l<<' '<<r<<endl;
     if(!a)
     {
          a=b;
          return;
     }
     else if(!b)
          return;
     if(l==r)
     {
          tree[a].mx+=tree[b].mx;
          tree[a].sum=l;
          return;
     }
     int mid=(l+r)>>1;
     add(l,mid,tree[a].l,tree[b].l);
     add(mid+1,r,tree[a].r,tree[b].r);
     up(a,tree[a].l,tree[a].r);
     return;
}
void dfs(int now,int deep)
{
    for(int i=1;i<=lg[deep];++i)
        par[now][i]=par[par[now][i-1]][i-1];
    d[now]=deep;
    for(int i=h[now];i;i=nex[i])
    {
        if(to[i]==par[now][0])
            continue;
        par[to[i]][0]=now;
        dfs(to[i],deep+1);
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    while(d[x]>d[y])
    x=par[x][lg[d[x]-d[y]]];
    if(x==y)
    return x;
    for(int i=lg[d[x]];i>=0;--i)
    {
        if(par[x][i]!=par[y][i])
        {
            x=par[x][i];
            y=par[y][i];
        }
    }
    return par[x][0];
}
void cal(int now,int fa)
{
    for(int i=h[now];i;i=nex[i])
    {
        if(to[i]==fa)
            continue;
        cal(to[i],now);
        add(1,N,rt[now],rt[to[i]]);
    }
    ans[now]=tree[rt[now]].sum;
    return;
}
int main()
{
  //   freopen("1.txt","r",stdin);
    int m;
     cin>>n>>m;
     for(int i=1;i<n;++i)
     {
          int a,b;
          scanf("%d%d",&a,&b);
          add(a,b);
          add(b,a);
     }
        lg[1]=0;
        for(int i=2;i<=n;++i)
        {
            if((i&(i-1))==0)
            lg[i]=lg[i-1]+1;
            else
            lg[i]=lg[i-1];
        }
     dfs(1,0);
     for(int i=1;i<=m;++i)
     {
          int a,b,c;scanf("%d%d%d",&a,&b,&c);
          change(1,N,rt[a],c,1);
          change(1,N,rt[b],c,1);
          int d=lca(a,b);
          change(1,N,rt[d],c,-1);
          change(1,N,rt[par[d][0]],c,-1);
     }
     cal(1,0);
     for(int i=1;i<=n;++i)
          printf("%d\n",ans[i]);
  //   printf("\n");
     return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值