2 条题解

  • 1
    @ 2023-1-18 11:53:59

    最近公共祖先【模板题】

    倍增

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 500000;
    const int MAXM = 500000;
    int n, q, root;          // 点数、询问个数、根节点
    vector<int> e[MAXN + 5]; // 存树
    int fa[MAXN + 5];
    int maxDep, dep[MAXN + 5];
    void dfs(int u, int father)
    {
        fa[u] = father;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == father)
                continue;
            dep[v] = dep[u] + 1;
            maxDep = max(maxDep, dep[v]);
            dfs(v, u);
        }
    }
    int lg2[MAXN + 5]; // 替代 log2(i)
    int dp[MAXN][35];  // i号点的 2^j 层祖先
    int lca(int u, int v)
    {
        if (dep[u] > dep[v])
            swap(u, v); // 让u的高度不低于v
        if (u == v)
            return u;
        // 把 v 提到与 u 同样高度
        for (int j = lg2[n]; j >= 0; j--)
            if ((1 << j) <= dep[v] - dep[u])
            {
                v = dp[v][j];
            }
        if (u == v)
            return u;
        // 找lca
        for (int j = lg2[n]; j >= 0; j--)
            if (dp[v][j] != dp[u][j])
            {
                v = dp[v][j],
                u = dp[u][j];
            }
        return dp[u][0];
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        //  输入
        cin >> n >> q >> root;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        // 预处理:最大层数 maxDep、每个点的深度、记录父节点、log2[]、dp数组
        dep[root] = 1;
        maxDep = 1;
        dfs(root, 0);
        lg2[1] = 0;
        for (int i = 2; i <= n; i++)
            // i == 2^(log2(i-1)+1)
            if (i == (1 << (lg2[i - 1] + 1)))
                lg2[i] = lg2[i - 1] + 1;
            else
                lg2[i] = lg2[i - 1];
        for (int j = 0; j <= lg2[n]; j++)
        {
            for (int i = 1; i <= n; i++)
            {
                // 求解 dp[i][j]
                if (j == 0)
                    dp[i][j] = fa[i];
                else
                    dp[i][j] = dp[dp[i][j - 1]][j - 1];
            }
        }
        // q 次询问
        while (q--)
        {
            int u, v;
            cin >> u >> v; // 求 lca(u,v)
            cout << lca(u, v) << "\n";
        }
        return 0;
    }
    

    欧拉序+st表

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 1000000;
    const int MAXM = 1000000;
    // 存图
    int n, m, s; // 点数、边数、根节点
    vector<int> e[MAXN + 5];
    // dfs每个点的深度
    int dep[MAXN * 2 + 5];
    // 欧拉序:euler[1]~euler[len]
    int len;
    int euler[MAXN * 2 + 5]; // 最多2n-1个点 dfs每个点的编号
    int pos[MAXN + 5];       // 每个编号第一次出现位置
    // rmq
    int l2[MAXN * 2 + 5];       // log2(i)
    int rmqd[MAXN * 2 + 5][30]; // euler[i] 开始的 2^j 个数的对应深度最小值
    int rmqa[MAXN * 2 + 5][30]; // 深度最小值的编号
    
    void dfs(int u, int fa)
    {
        euler[++len] = u;
        pos[u] = len;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == fa)
                continue;
            dep[v] = dep[u] + 1;
            dfs(v, u);
            euler[++len] = u;
        }
    }
    
    void rmqinit()
    {
        l2[0] = -1;
        for (int i = 1; i <= len; i++)
        {
            l2[i] = l2[i - 1];
            if ((1 << (l2[i] + 1)) == i)
                l2[i]++;
            rmqa[i][0] = euler[i];
            rmqd[i][0] = dep[euler[i]];
        }
        for (int j = 1; j <= l2[len]; j++)
        {
            for (int i = 1; i <= len - (1 << j) + 1; i++)
            {
                if (rmqd[i][j - 1] < rmqd[i + (1 << (j - 1))][j - 1])
                {
                    rmqd[i][j] = rmqd[i][j - 1];
                    rmqa[i][j] = rmqa[i][j - 1];
                }
                else
                {
                    rmqd[i][j] = rmqd[i + (1 << (j - 1))][j - 1];
                    rmqa[i][j] = rmqa[i + (1 << (j - 1))][j - 1];
                }
            }
        }
    }
    
    int query(int l, int r)
    {
        l = pos[l];
        r = pos[r];
        if (l > r)
            swap(l, r);
        int k = l2[r - l + 1];
        if (rmqd[l][k] < rmqd[r - (1 << k) + 1][k])
            return rmqa[l][k];
        else
            return rmqa[r - (1 << k) + 1][k];
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m >> s;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        // 构建欧拉序
        len = 0;
        dep[s] = 1;
        dfs(s, 0);
        // rmq初始化
        rmqinit();
        for (int i = 1; i <= m; i++)
        {
            int l, r;
            cin >> l >> r;
            cout << query(l, r) << "\n";
        }
        return 0;
    }
    

    tarjan(离线+并查集)

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 1000000;
    const int MAXM = 1000000;
    int n, m, s;
    vector<int> e[MAXN + 5];
    //(v, 问题 id)
    vector<pair<int, int> > ask[MAXN + 5];
    int ans[MAXM + 5]; // 问题 id 为 i 的问题答案
    
    // 每个点是否搜过了
    bool vis[MAXN + 5];
    // 并查集
    int fa[MAXN + 5];
    int findFa(int x)
    {
        if (fa[x] == x)
            return x;
        else
            return fa[x] = findFa(fa[x]);
    }
    void dfs(int u, int from)
    {
        for (int i = 0; i < ask[u].size(); i++)
            if (vis[ask[u][i].first])
                ans[ask[u][i].second] = findFa(ask[u][i].first);
        vis[u] = true;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == from)
                continue;
            dfs(v, u);
            fa[v] = u;
        }
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m >> s;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        memset(ans, 0, sizeof(ans));
        // 读询问
        for (int i = 1; i <= m; i++)
        {
            int u, v, id;
            cin >> u >> v;
            id = i;
            if (u == v)
                ans[i] = u;
            else
            {
                ask[u].push_back(make_pair(v, id));
                ask[v].push_back(make_pair(u, id));
            }
        }
        // tarjan
        for (int i = 1; i <= n; i++)
        {
            fa[i] = i;
            vis[i] = false;
        }
        dfs(s, 0);
        for (int i = 1; i <= m; i++)
            cout << ans[i] << "\n";
        return 0;
    }
    

    树链剖分

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 500000 + 5;
    int n, m, s;
    vector<int> e[MAXN];
    //每个点的:父节点、深度、大小、重子节点
    int fa[MAXN], dep[MAXN], siz[MAXN], hson[MAXN];
    void dfs_build(int u, int fat)
    {
        hson[u] = 0;
        siz[hson[u]] = 0;
        siz[u] = 1;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == fat)
                continue;
            dep[v] = dep[u] + 1;
            fa[v] = u;
            dfs_build(v, u);
            siz[u] += siz[v];
            if (siz[v] > siz[hson[u]])
                hson[u] = v;
        }
    }
    //每个点的:所在链的链顶、重边优先的 dfs 序、dfs序对应的节点编号
    int tot, top[MAXN], dfn[MAXN], rnk[MAXN];
    void dfs_div(int u, int fa)
    {
        dfn[u] = ++tot;
        rnk[tot] = u;
        if (hson[u])
        {
            top[hson[u]] = top[u];
            dfs_div(hson[u], u);
            for (int i = 0; i < e[u].size(); i++)
            {
                int v = e[u][i];
                if (v == fa || v == hson[u])
                    continue;
                top[v] = v;
                dfs_div(v, u);
            }
        }
    }
    int lca(int u, int v)
    {
        while (top[u] != top[v])
        {
            if (dep[top[u]] > dep[top[v]])
                u = fa[top[u]];
            else
                v = fa[top[v]];
        }
        return dep[u] > dep[v] ? v : u;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m >> s;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dep[s] = 1;
        fa[s] = 0;
        dfs_build(s, 0);
        tot = 0;
        top[s] = s;
        dfs_div(s, 0);
        while (m--)
        {
            int u, v;
            cin >> u >> v;
            cout << lca(u, v) << "\n";
        }
        return 0;
    }
    
    • 0
      @ 2023-1-29 14:49:07

      次小生成树

      严格次小生成树

      #include <bits/stdc++.h>
      #define int long long
      using namespace std;
      const int MAXN = 500000;
      const int MAXM = 500000;
      // 传入两个最大次大值,返回四个数的最大次大值
      pair<int, int> getMaxW(pair<int, int> a, pair<int, int> b)
      {
          if (a.first == b.first)
              return make_pair(a.first, max(a.second, b.second));
          if (a.first > b.first)
              return make_pair(a.first, max(a.second, b.first));
          if (a.first < b.first)
              return make_pair(b.first, max(a.first, b.second));
      }
      //-----------封装LCA
      struct LCA
      {
          int n, root;             // 点数、询问个数、根节点
          vector<int> e[MAXN + 5]; // 存树
          int fa[MAXN + 5];
          int maxDep, dep[MAXN + 5];
          int lg2[MAXN + 5];             // 替代 log2(i)
          int dp[MAXN][35];              // i号点的 2^j 层祖先
          vector<int> ew[MAXN + 5];      // 树的边权 -----
          pair<int, int> maxW[MAXN][35]; // i号点的 2^j 层最大次大边权-----
          // 图的初始化
          void init(int _n, int _root)
          {
              n = _n;
              root = _root;
              for (int i = 1; i <= n; i++)
                  e[i].clear();
          }
          // 加边
          void addEdge(int u, int v, int w)
          {
              e[u].push_back(v);
              e[v].push_back(u);
              ew[u].push_back(w); //-----
              ew[v].push_back(w); //-----
          }
          // dfs 得到父子关系、深度
          void dfs(int u, int father)
          {
              fa[u] = father;
              for (int i = 0; i < e[u].size(); i++)
              {
                  int v = e[u][i];
                  int w = ew[u][i]; //-----
                  if (v == father)
                      continue;
                  dep[v] = dep[u] + 1;
                  maxW[v][0] = make_pair(w, -1); //------
                  maxDep = max(maxDep, dep[v]);
                  dfs(v, u);
              }
          }
          // lca 的初始化
          void init_lca()
          {
              // 预处理:最大层数 maxDep、每个点的深度、记录父节点、log2[]、dp数组
              dep[root] = 1;
              maxDep = 1;
              dfs(root, 0);
              lg2[1] = 0;
              for (int i = 2; i <= n; i++)
                  // i == 2^(log2(i-1)+1)
                  if (i == (1 << (lg2[i - 1] + 1)))
                      lg2[i] = lg2[i - 1] + 1;
                  else
                      lg2[i] = lg2[i - 1];
              for (int j = 0; j <= lg2[n]; j++)
              {
                  for (int i = 1; i <= n; i++)
                  {
                      // 求解 dp[i][j]
                      if (j == 0)
                          dp[i][j] = fa[i];
                      else
                          dp[i][j] = dp[dp[i][j - 1]][j - 1];
                      // 求解 maxW[i][j]
                      if (j > 0)
                          maxW[i][j] = getMaxW(maxW[i][j - 1],
                                               maxW[dp[i][j - 1]][j - 1]);
                  }
              }
          }
          // 查询两个点的lca
          int lca(int u, int v)
          {
              if (dep[u] > dep[v])
                  swap(u, v); // 让u的高度不低于v
              if (u == v)
                  return u;
              // 把 v 提到与 u 同样高度
              for (int j = lg2[n]; j >= 0; j--)
                  if ((1 << j) <= dep[v] - dep[u])
                  {
                      v = dp[v][j];
                  }
              if (u == v)
                  return u;
              // 找lca
              for (int j = lg2[n]; j >= 0; j--)
                  if (dp[v][j] != dp[u][j])
                  {
                      v = dp[v][j],
                      u = dp[u][j];
                  }
              return dp[u][0];
          }
          pair<int, int> queryMaxW(int u, int v)
          {
              int uv = lca(u, v);
              pair<int, int> res = make_pair(-1, -1);
              // 把 u 提到与 uv 同样高度
              for (int j = lg2[n]; j >= 0; j--)
                  if ((1 << j) <= dep[u] - dep[uv])
                  {
                      res = getMaxW(res, maxW[u][j]);
                      u = dp[u][j];
                  }
              // 把 v 提到与 uv 同样高度
              for (int j = lg2[n]; j >= 0; j--)
                  if ((1 << j) <= dep[v] - dep[uv])
                  {
                      res = getMaxW(res, maxW[v][j]);
                      v = dp[v][j];
                  }
              return res;
          }
      } lca;
      //-----------封装Kruskal
      struct kEdge
      {
          int u, v, w;
          bool cho; // 是否被选中了
      };
      bool operator<(const kEdge &a, const kEdge &b)
      {
          return a.w < b.w;
      }
      struct Kruskal
      {
          int n, m, ans;
          vector<kEdge> e; // 存所有的边
          int fa[MAXN + 5];
          int findFa(int x)
          {
              if (x == fa[x])
                  return x;
              return fa[x] = findFa(fa[x]);
          }
          void init(int _n, int _m)
          {
              n = _n;
              m = _m;
              ans = 0;
              for (int i = 1; i <= n; i++)
                  fa[i] = i;
          }
          void addEdge(int u, int v, int w)
          {
              e.push_back((kEdge){u, v, w, false});
          }
          void run()
          {
              sort(e.begin(), e.end());
              for (int i = 0; i < e.size(); i++)
              {
                  int u = e[i].u;
                  int v = e[i].v;
                  int w = e[i].w;
                  int fU = findFa(u);
                  int fV = findFa(v);
                  if (fU == fV)
                      continue;
                  e[i].cho = true; // 这条边被选择了
                  ans += e[i].w;
                  fa[fU] = fV;
              }
          }
      } kruskal;
      signed main()
      {
          ios::sync_with_stdio(false);
          cin.tie(0);
          int n, m;
          cin >> n >> m;
          kruskal.init(n, m);
          for (int i = 1; i <= m; i++)
          {
              int u, v, w;
              cin >> u >> v >> w;
              kruskal.addEdge(u, v, w);
          }
          kruskal.run();
          lca.init(n, 1);
          for (int i = 0; i < kruskal.e.size(); i++)
              if (kruskal.e[i].cho)
                  lca.addEdge(kruskal.e[i].u, kruskal.e[i].v, kruskal.e[i].w);
          lca.init_lca();
      
          int ans = 1e18;
          for (int i = 0; i < kruskal.e.size(); i++)
              if (!kruskal.e[i].cho)
              {
                  pair<int, int> maxW = lca.queryMaxW(kruskal.e[i].u, kruskal.e[i].v);
                  int m1 = maxW.first;
                  int m2 = maxW.second;
                  if (m1 == kruskal.e[i].w && m2 != -1)
                      ans = min(ans, kruskal.ans - m2 + kruskal.e[i].w);
                  else if (m1 != kruskal.e[i].w && m1 != -1)
                      ans = min(ans, kruskal.ans - m1 + kruskal.e[i].w);
              }
          cout << ans << "\n";
          return 0;
      }
      
      
      
      • 1

      信息

      ID
      1211
      时间
      1000ms
      内存
      256MiB
      难度
      3
      标签
      递交数
      22
      已通过
      20
      上传者