1. 程式人生 > >學習筆記--樹上差分

學習筆記--樹上差分

mat 覆蓋 [1] reg code out getch 相關 \n

  • 前言

    在做一些樹上路徑修改&查詢相關題目時,有時我們用不著樹鏈剖分,類比於序列上的差分,我們可以進行樹上差分,不過情況稍有些不同,分為點值上的差分和邊權上的差分兩種

  • 點值差分

    對樹上路徑\(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 <iostream>
    #include <vector>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <ctime>
    #include <algorithm>
    #define ll long long 
    #define ri register int 
    using namespace std;
    const int maxn=50005;
    const int inf=0x7fffffff;
    template <class T>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(v==fa[now]||v==son[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\n",ans);
      //printf("%lf\n",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 <iostream>
    #include <vector>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <ctime>
    #include <algorithm>
    #define ll long long 
    #define ri register int 
    using namespace std;
    const int maxn=100005;
    const int inf=0x7fffffff;
    template <class T>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(v==fa[now]||v==son[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(v==fa[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\n",ans);
      return 0;
    }
  • 例題待填坑

    • 貨車運輸

    • 天天愛跑步

學習筆記--樹上差分