dfs序基础+树上差分(树链剖分)

· · 个人记录

基础

对于一些树上问题,如果不转化就很难求解。可以考虑转化为区间问题。

dfs序

可以发现,如果点 x 的 dfn 被点 y 入栈和出栈的时间戳完全覆盖,则 x 一定在 y 的一个子树内。

树上差分

普通差分:序列上进行差分。

数上差分:子树上进行差分。

点差分的基础题型:给定一棵树,把 st 的路径的点加上 v

树上差分需要加减四处:

diff[x]++ ;
diff[y]++ ;
diff[Lca]-- ;
diff[fa]-- ;

例题

T664766 dfs序基础1

先把,每个点都替换为他的 dfn。

对于修改,用树状数组维护即可,注意,修改的是他的新编号。

最后是查询,用树状数组求,然后减掉多余部分即可。

//Code by hhy
#include<bits/stdc++.h>
//#define int long long
#define ull unsigned long long
#define pb push_back
#define fi first
#define se second
#define ll long long
using namespace std ;
const int kMaxN = 1e6 + 5 , MOD = 998244353 , INF = 1e18 ;
struct Edgepr
{
    int u , w ;
};
struct Edgeve
{
    int v , w ;
};
struct node
{
  int x , y , id ;
}Node[kMaxN] ;
int n , m , r ;
int a[kMaxN] , s[kMaxN] , e[kMaxN] ;
long long tre[kMaxN << 1] ;
int tim ;
vector<int> G[kMaxN] ;
inline ll ksm(ll a , ll b)
{
    ll mul = 1 ;
    while(b)
    {
        if(b & 1) mul *= a , mul %= MOD ;
        a *= a ;
        a %= MOD ;
        b >>= 1 ;
    }
    return mul ;
}
inline int lowbit(int x)
{
    return (x & (-x)) ;
}
long long query(int x)
{
    long long ans = 0 ;
    while(x > 0)
    {
        ans += tre[x] ;
        x -= lowbit(x) ;
    }
    return ans ;
}
void update(int x , int vis)
{
    while(x <= n)
    {
        tre[x] += vis ;
        x += lowbit(x) ;
    }
}
void dfs(int u , int fa)
{
    s[u] = ++tim ;
    update(s[u] , a[u]) ;
    for( int i = 0 ; i < G[u].size() ; i++ )
    {
        int v = G[u][i] ;
        if(v == fa) continue ;
        dfs(v , u) ;
    }
    e[u] = tim ;
}
void work()
{
  cin >> n >> m >> r ;
  for( int i = 1 ; i <= n ; i++ ) cin >> a[i] ;
  for( int i = 1 ; i < n ; i++ )
  {
    int u , v ;
    cin >> u >> v ;
    G[u].push_back(v) ;
    G[v].push_back(u) ;
    }
    dfs(r , 0) ;
    for( int tt = 1 ; tt <= m ; tt++ )
    {
        int op ;
        cin >> op ;
        if(op == 1)
        {
            int t , x ;
            cin >> t >> x ;
            update(s[t] , x) ;
        }
        else
        {
            int t ;
            cin >> t ;
            cout << query(e[t]) - query(s[t] - 1) << "\n" ;
        }
    }
}
signed main()
{
//  freopen(".in" , "r" , stdin) ;
//  freopen(".out" , "w" , stdout) ;
    ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ;
  int t = 1 ;
  // cin >> t ;
  while(t--) work() ;
  return 0 ;
}

T664767 dfs序基础2

改变的地方就是需要区间修改,用线段树维护即可。

//Code by hhy
#include<bits/stdc++.h>
//#define int long long
#define ull unsigned long long
#define pb push_back
#define fi first
#define se second
#define ll long long
using namespace std ;
const int kMaxN = 1e6 + 5 , MOD = 998244353 , INF = 1e18 ;
struct Edgepr
{
    int u , w ;
};
struct Edgeve
{
    int v , w ;
};
struct node
{
  int x , y , id ;
}Node[kMaxN] ;
int n , m , r ;
int a[kMaxN] , s[kMaxN] , e[kMaxN] ;
long long tre[kMaxN << 2] ;
long long lazy[kMaxN << 2] ;
int num[kMaxN] ;
int tim ;
vector<int> G[kMaxN] ;
inline ll ksm(ll a , ll b)
{
    ll mul = 1 ;
    while(b)
    {
        if(b & 1) mul *= a , mul %= MOD ;
        a *= a ;
        a %= MOD ;
        b >>= 1 ;
    }
    return mul ;
}
#define lson(x) x << 1 
#define rson(x) x << 1 | 1
void push_up(int x)
{
    tre[x] = tre[lson(x)] + tre[rson(x)] ;
}
void push_down(int l , int r , int k)
{
    if(lazy[k])
    {
        int mid = l + r >> 1 ;
        lazy[lson(k)] += lazy[k] ;
        lazy[rson(k)] += lazy[k] ;
        tre[lson(k)] += (mid - l + 1) * lazy[k] ;
        tre[rson(k)] += (r - mid) * lazy[k] ;
        lazy[k] = 0 ;
    }
}
void build(int l , int r , int k)
{
    if(l == r)
    {
        tre[k] = a[num[l]] ;
        return ;
    }
    int mid = l + r >> 1 ;
    build(l , mid , lson(k)) ;
    build(mid + 1 , r , rson(k)) ;
    push_up(k) ;
}
void update(int l , int r , int k , int L , int R , long long c)
{
    if(L <= l && r <= R)
    {
        tre[k] += (r - l + 1) * c ;
        lazy[k] += c ;
        return ;
    }
    push_down(l , r , k) ;
    int mid = l + r >> 1 ;
    if(L <= mid) update(l , mid , lson(k) , L , R , c) ;
    if(R > mid) update(mid + 1 , r , rson(k) , L , R , c) ;
    push_up(k) ;
}
long long query(int l , int r , int k , int L , int R)
{
    if(L <= l && r <= R)
    {
        return tre[k] ;
    }
    push_down(l , r , k) ;
    int mid = l + r >> 1 ;
    long long res = 0 ;
    if(L <= mid) res += query(l , mid , lson(k) , L , R) ;
    if(R > mid) res += query(mid + 1 , r , rson(k) , L , R) ;
    return res ;
}
void dfs(int u , int fa)
{
    s[u] = ++tim ;
    num[s[u]] = u ;
    for( int i = 0 ; i < G[u].size() ; i++ )
    {
        int v = G[u][i] ;
        if(v == fa) continue ;
        dfs(v , u) ;
    }
    e[u] = tim ;
}
void work()
{
  cin >> n >> m >> r ;
  for( int i = 1 ; i <= n ; i++ ) cin >> a[i] ;
  for( int i = 1 ; i < n ; i++ )
  {
    int u , v ;
    cin >> u >> v ;
    G[u].push_back(v) ;
    G[v].push_back(u) ;
    }
    dfs(r , 0) ;
    build(1 , n , 1) ;
    for( int tt = 1 ; tt <= m ; tt++ )
    {
        int op ;
        cin >> op ;
        if(op == 1)
        {
            int t , x ;
            cin >> t >> x ;
            update(1 , n , 1 , s[t] , e[t] , x) ;
        }
        else
        {
            int t ;
            cin >> t ;
            cout << query(1 , n , 1 , s[t] , e[t]) << "\n" ;
        }
    }
}
signed main()
{
//  freopen(".in" , "r" , stdin) ;
//  freopen(".out" , "w" , stdout) ;
    ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ;
  int t = 1 ;
  // cin >> t ;
  while(t--) work() ;
  return 0 ;
}

P2982 [USACO10FEB] Slowing down G

是区间修改,单点查询,所以可以用树状数组维护即可。

//Code by hhy
#include<bits/stdc++.h>
//#define int long long
#define ull unsigned long long
#define pb push_back
#define fi first
#define se second
#define ll long long
using namespace std ;
const int kMaxN = 1e6 + 5 , MOD = 998244353 , INF = 1e18 ;
struct Edgepr
{
    int u , w ;
};
struct Edgeve
{
    int v , w ;
};
struct node
{
  int x , y , id ;
}Node[kMaxN] ;
int n ;
int a[kMaxN] , s[kMaxN] , e[kMaxN] ;
long long tre[kMaxN << 2] ;
long long lazy[kMaxN << 2] ;
int num[kMaxN] ;
int tim ;
vector<int> G[kMaxN] ;
inline ll ksm(ll a , ll b)
{
    ll mul = 1 ;
    while(b)
    {
        if(b & 1) mul *= a , mul %= MOD ;
        a *= a ;
        a %= MOD ;
        b >>= 1 ;
    }
    return mul ;
}
inline int lowbit(int x)
{
    return (x & (-x)) ;
}
long long query(int x)
{
    long long ans = 0 ;
    while(x > 0)
    {
        ans += tre[x] ;
        x -= lowbit(x) ;
    }
    return ans ;
}
void update(int x , int vis)
{
    while(x <= n)
    {
        tre[x] += vis ;
        x += lowbit(x) ;
    }
}
void dfs(int u , int fa)
{
    s[u] = ++tim ;
    for( int i = 0 ; i < G[u].size() ; i++ )
    {
        int v = G[u][i] ;
        if(v == fa) continue ;
        dfs(v , u) ;
    }
    e[u] = tim ;
}
void work()
{
  cin >> n ;
  for( int i = 1 ; i < n ; i++ )
  {
    int u , v ;
    cin >> u >> v ;
    G[u].push_back(v) ;
    G[v].push_back(u) ;
    }
    dfs(1 , 0) ;
    for( int i = 1 ; i <= n ; i++ )
    {
        int x ;
        cin >> x ;
//      cout << s[x] << " " << e[x] << " " ;
        cout << query(s[x]) << "\n" ;
        update(e[x] + 1 , -1) ;
        update(s[x] , 1) ;
    }
}
signed main()
{
//  freopen(".in" , "r" , stdin) ;
//  freopen(".out" , "w" , stdout) ;
    ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ;
  int t = 1 ;
  // cin >> t ;
  while(t--) work() ;
  return 0 ;
}

P3128 [USACO15DEC] Max Flow P

模版题。

求出每个点的差分值后,记得要把整个子树的加起来才是答案。

//Code by hhy
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define pb push_back
#define fi first
#define se second
#define ll long long
using namespace std ;
const int kMaxN = 3e5 + 5 , MOD = 998244353 , INF = 1e18 ;
struct Edgepr
{
    int u , w ;
};
struct Edgeve
{
    int v , w ;
};
struct node
{
  int x , y , id ;
}Node[kMaxN] ;
int n , k ;
int a[kMaxN] , d[kMaxN] , sum[kMaxN] ;
int dep[kMaxN] , dp[kMaxN][25] ;
vector<int> G[kMaxN] ;
inline ll ksm(ll a , ll b)
{
    ll mul = 1 ;
    while(b)
    {
        if(b & 1) mul *= a , mul %= MOD ;
        a *= a ;
        a %= MOD ;
        b >>= 1 ;
    }
    return mul ;
}
void dfs1(int u , int fa)
{
  dep[u] = dep[fa] + 1 ;
  dp[u][0] = fa ;
  for( int i = 1 ; (1 << i) <= dep[u] ; i++ )
  {
    dp[u][i] = dp[dp[u][i - 1]][i - 1] ;
  }
  for( int i = 0 ; i < G[u].size() ; i++ )
  {
    if(G[u][i] != fa) dfs1(G[u][i] , u) ;
  }
}
int lca(int x , int y)
{
  if(dep[x] > dep[y]) swap(x , y) ;
    for( int i = 20 ; i >= 0 ; i-- )
    {
        if(dep[dp[y][i]] >= dep[x]) y = dp[y][i] ;
    }
    if(y == x)
    {
        return x ;
    }
    for( int i = 20 ; i >= 0 ; i-- )
    {
        if(dp[y][i] != dp[x][i]) y = dp[y][i] , x = dp[x][i] ;
    }
    return dp[x][0] ;
}
void dfs2(int u , int fa)
{
    sum[u] = d[u] ;
    for( int i = 0 ; i < G[u].size() ; i++ )
  {
    if(G[u][i] != fa) 
        {
            dfs2(G[u][i] , u) ;
            sum[u] += sum[G[u][i]] ;
        }
  }
}
void work()
{
  cin >> n >> k ;
  for( int i = 1 ; i < n ; i++ )
  {
    int u , v ;
    cin >> u >> v ;
    G[u].push_back(v) ;
    G[v].push_back(u) ;
    }
    dfs1(1 , 0) ;
    for( int i = 1 ; i <= k ; i++ )
    {
        int x , y ;
        cin >> x >> y ;
        int Lca = lca(x , y) ;
        int fa = dp[Lca][0] ;
        d[x]++ ;
        d[y]++ ;
        d[Lca]-- ;
        d[fa]-- ;
    }
    dfs2(1 , 0) ;
    int maxn = 0 ;
    for( int i = 1 ; i <= n ; i++ ) maxn = max(maxn , sum[i]) ;
    cout << maxn ;
}
signed main()
{
//  freopen(".in" , "r" , stdin) ;
//  freopen(".out" , "w" , stdout) ;
    ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ;
  int t = 1 ;
  // cin >> t ;
  while(t--) work() ;
  return 0 ;
}