A11【模板】树上差分

A11【模板】树上差分_哔哩哔哩_bilibili

 

前缀和 & 差分 - OI Wiki

 

D09 倍增算法 P3379【模板】最近公共祖先(LCA) - 董晓 - 博客园

P3128 [USACO15DEC] Max Flow P - 洛谷

// 
#include<bits/stdc++.h>
using namespace std;

const int N=50005,M=2*N;
int h[N],to[M],ne[M],tot;
void add(int x,int y){
  to[++tot]=y,ne[tot]=h[x],h[x]=tot;
}

int n,m,ans,power[N];
int dep[N],f[N][22];
void dfs(int x,int fa){ 
  dep[x]=dep[fa]+1,f[x][0]=fa;
  for(int i=1; i<=20; i++) f[x][i]=f[f[x][i-1]][i-1];
  for(int i=h[x]; i; i=ne[i]){
    int y=to[i];
    if(y!=fa) dfs(y,x);
  }
}
int lca(int x,int y){
  if(dep[x]<dep[y])swap(x,y);
  for(int i=20; ~i; i--)if(dep[f[x][i]]>=dep[y]) x=f[x][i];
  if(x==y) return y;
  for(int i=20; ~i; i--)if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
  return f[x][0];
}
void dfs2(int x,int fa){
  for(int i=h[x]; i; i=ne[i]){
    int y=to[i];
    if(y!=fa){
      dfs2(y,x);
      power[x]+=power[y]; //差分的子树和
    }
  }
  ans=max(ans,power[x]);
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1,x,y;i<n;++i){
    scanf("%d%d",&x,&y);
    add(x,y); add(y,x);
  }
  
  dfs(1,0); //倍增预处理 dep,fa 数组
  for(int i=1,x,y; i<=m; ++i){
    scanf("%d%d",&x,&y);
    int z=lca(x,y); //倍增求lca
    ++power[x];++power[y]; 
    --power[z];--power[f[z][0]]; //树上点差分
  }
  dfs2(1,0); //统计答案
  printf("%d\n",ans);
}

 

练习:

U143800 暗之连锁 - 洛谷
// LCA+树上差分 O(mlogn)
#include<bits/stdc++.h>
using namespace std;

const int N=100010,M=N<<1;
int n,m,ans;
int h[N],to[M],ne[M],idx;
int dep[N],fa[N][17],d[N]; //d[x]表示节点x到根节点的距离,即该路径上边被覆盖的次数

void add(int u,int v){
  to[++idx]=v,ne[idx]=h[u],h[u]=idx;
}
void bfs(){ //预处理dep,fa数组
  dep[1]=1;
  queue<int> q; q.push(1);
  while(q.size()){
    int u=q.front(); q.pop();
    for(int i=h[u]; i; i=ne[i]){
      int v=to[i];
      if(dep[v]) continue;
      dep[v]=dep[u]+1;
      q.push(v);
      fa[v][0]=u;
      for(int k=1; k<=16; k++)
        fa[v][k]=fa[fa[v][k-1]][k-1];
    }
  }
}
int lca(int u,int v){
  if(dep[u]<dep[v]) swap(u,v);
  for(int k=16; k>=0; k--)
    if(dep[fa[u][k]]>=dep[v]) u=fa[u][k];
  if(u==v) return u;
  for(int k=16; k>=0; k--)
    if(fa[u][k]!=fa[v][k]) u=fa[u][k], v=fa[v][k];
  return fa[u][0];
}
int dfs(int u,int father){ //对子树的差分求和
  int sum=d[u];
  for(int i=h[u]; i; i=ne[i]){
    int j=to[i];
    if(j==father) continue;
    int s=dfs(j,u);
    if(s==0) ans+=m; //统计方案数
    else if(s==1) ans++;
    sum+=s;
  }
  return sum; //u子树的结点权值和,即(u,fa)边覆盖次数
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=0,u,v; i<n-1; i++){
    scanf("%d%d",&u,&v);
    add(u,v),add(v,u);
  }
  bfs();
  for(int i=0,u,v; i<m; i++){
    scanf("%d%d",&u,&v);
    int p=lca(u,v);
    d[u]++,d[v]++,d[p]-=2; //树上差分
  }
  dfs(1,0); //差分求和
  printf("%d\n",ans);
}

 

Luogu P3258 [JLOI2014] 松鼠的新家

Luogu P2680 [NOIP2015 提高组] 运输计划

Luogu P1600 [NOIP2016 提高组] 天天爱跑步

Luogu P4556 [Vani有约会] 雨天的尾巴

 

posted @ 2023-07-04 17:33  董晓  阅读(1243)  评论(0)    收藏  举报