zoukankan      html  css  js  c++  java
  • DSU模板(树的启发式合并)

    摘自Codeforces博客

    With dsu on tree we can answer queries of this type:

    How many vertices in subtree of vertex v has some property in O(n lg n) time (for all of the queries).

    For example:

    Given a tree, every vertex has color. Query is how many vertices in subtree of vertex v are colored with color c?

    要点:记录每棵子树的大小,启发式合并。

    模板整理如下:

    1. easy to code but 

     1 //O(nlognlogn)
     2 map<int, int> *cnt[maxn];
     3 void dfs(int v, int p){
     4     int mx = -1, bigChild = -1;
     5     for(auto u : g[v])
     6        if(u != p){
     7            dfs(u, v);
     8            if(sz[u] > mx)
     9                mx = sz[u], bigChild = u;
    10        }
    11     if(bigChild != -1)
    12         cnt[v] = cnt[bigChild];
    13     else
    14         cnt[v] = new map<int, int> ();
    15     (*cnt[v])[ col[v] ] ++;
    16     for(auto u : g[v])
    17        if(u != p && u != bigChild){
    18            for(auto x : *cnt[u])
    19                (*cnt[v])[x.first] += x.second;
    20        }
    21     //now (*cnt)[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    22 
    23 }
    View Code

    2. easy to code and 

     1 vector<int> *vec[maxn];
     2 int cnt[maxn];
     3 void dfs(int v, int p, bool keep){
     4     int mx = -1, bigChild = -1;
     5     for(auto u : g[v])
     6        if(u != p && sz[u] > mx)
     7            mx = sz[u], bigChild = u;
     8     for(auto u : g[v])
     9        if(u != p && u != bigChild)
    10            dfs(u, v, 0);
    11     if(bigChild != -1)
    12         dfs(bigChild, v, 1), vec[v] = vec[bigChild];
    13     else
    14         vec[v] = new vector<int> ();
    15     vec[v]->push_back(v);
    16     cnt[ col[v] ]++;
    17     for(auto u : g[v])
    18        if(u != p && u != bigChild)
    19            for(auto x : *vec[u]){
    20                cnt[ col[x] ]++;
    21                vec[v] -> push_back(x);
    22            }
    23     //now (*cnt)[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    24     // note that in this step *vec[v] contains all of the subtree of vertex v.
    25     if(keep == 0)
    26         for(auto u : *vec[v])
    27             cnt[ col[u] ]--;
    28 }
    View Code

    3. heavy-light decomposition style 

     1 int cnt[maxn];
     2 bool big[maxn];
     3 void add(int v, int p, int x){
     4     cnt[ col[v] ] += x;
     5     for(auto u: g[v])
     6         if(u != p && !big[u])
     7             add(u, v, x)
     8 }
     9 void dfs(int v, int p, bool keep){
    10     int mx = -1, bigChild = -1;
    11     for(auto u : g[v])
    12        if(u != p && sz[u] > mx)
    13           mx = sz[u], bigChild = u;
    14     for(auto u : g[v])
    15         if(u != p && u != bigChild)
    16             dfs(u, v, 0);  // run a dfs on small childs and clear them from cnt
    17     if(bigChild != -1)
    18         dfs(bigChild, v, 1), big[bigChild] = 1;  // bigChild marked as big and not cleared from cnt
    19     add(v, p, 1);
    20     //now cnt[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    21     if(bigChild != -1)
    22         big[bigChild] = 0;
    23     if(keep == 0)
    24         add(v, p, -1);
    25 }
    View Code

    4. My invented style 

     1 This implementation for "Dsu on tree" technique is new and invented by me. This implementation is easier to code than others. Let st[v] dfs starting time of vertex v, ft[v] be it's finishing time and ver[time] is the vertex which it's starting time is equal to time.
     2 
     3 int cnt[maxn];
     4 void dfs(int v, int p, bool keep){
     5     int mx = -1, bigChild = -1;
     6     for(auto u : g[v])
     7        if(u != p && sz[u] > mx)
     8           mx = sz[u], bigChild = u;
     9     for(auto u : g[v])
    10         if(u != p && u != bigChild)
    11             dfs(u, v, 0);  // run a dfs on small childs and clear them from cnt
    12     if(bigChild != -1)
    13         dfs(bigChild, v, 1);  // bigChild marked as big and not cleared from cnt
    14     for(auto u : g[v])
    15     if(u != p && u != bigChild)
    16         for(int p = st[u]; p < ft[u]; p++)
    17         cnt[ col[ ver[p] ] ]++;
    18     cnt[ col[v] ]++;
    19     //now cnt[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    20     if(keep == 0)
    21         for(int p = st[v]; p < ft[v]; p++)
    22         cnt[ col[ ver[p] ] ]--;
    23 }
    View Code

    时间复杂度分析

    4. My invented style

    考虑每个节点最多会被清零几次?

    显然被清零的次数即为该节点到根节点的链上轻儿子的个数

    联系树链剖分,任意一个节点到根节点的链最多被划分为O(logn)段重链,最多被清零O(logn)次.

    故时间复杂度为O(nlogn).

    2, 3, 4时间复杂度分析类似.

    启发式合并算法复杂度总结

    线段树合并 => O(nlogU), U为线段树权值域,从势能分析,合并的复杂度为O(总节点数减小的个数)

    平衡树合并+树链剖分+单次插入删除O(logn) => O(nlognlogn)

    平衡树合并+树链剖分+单次插入删除O(1) => O(nlogn)

    set合并 => O(nlognlogn), 显然set合并复杂度不会高于multiset(平衡树)合并复杂度

  • 相关阅读:
    bzoj3272 Zgg吃东西
    bzoj3894 文理分科
    poj1149 PIGS
    poj1637 Sightseeing tour
    [Wc2007]剪刀石头布
    poj2396 Budget
    [NOI2017]游戏
    CF666E Forensic Examination
    bzoj4889 [Tjoi2017]不勤劳的图书管理员
    CF587F Duff is Mad
  • 原文地址:https://www.cnblogs.com/dirge/p/6628799.html
Copyright © 2011-2022 走看看