【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目(二)

简介: 【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目

【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目(一)https://developer.aliyun.com/article/1478723


换根法DFS

树中删除一个节点,则各孩子各一个连通区域,除自己及后代外一个区域。如果这个节点是根,则简单得多。各孩子一个连通区域。

DSF(cur) 返回自己及子孙到当前根节点距离是signalSpeed 倍的节点数量。令当前根节点各孩子的返回值是{i1,i2,⋯ \cdots,im} 。i1*i2+(i1+i2)*i3 ⋯ \cdots +(I1+i2+… \dots + im − 1 _{m-1}m1)*im。这样不必除以二。

a

class CNeiBo
{
public: 
  static vector<vector<int>> Two(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) 
  {
    vector<vector<int>>  vNeiBo(n);
    for (const auto& v : edges)
    {
      vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
      if (!bDirect)
      {
        vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
      }
    }
    return vNeiBo;
  } 
  static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
  {
    vector<vector<std::pair<int, int>>> vNeiBo(n);
    for (const auto& v : edges)
    {
      vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
      if (!bDirect)
      {
        vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
      }
    }
    return vNeiBo;
  }
};
class Solution {
public:
  vector<int> countPairsOfConnectableServers(vector<vector<int>>& edges, int signalSpeed) {
    m_c = edges.size() + 1;
    m_iSignalSpeed = signalSpeed;   
    auto neiBo = CNeiBo::Three(m_c, edges, false, 0);   
    vector<int> vRet(m_c);
    for (int c = 0; c < m_c; c++)
    {
      int& iRet = vRet[c];
      int left = 0;
      for (const auto& [next, len] : neiBo[c])
      {
        int cur = DFS(neiBo, next, c, len);
        iRet += left * cur;
        left += cur;
      }
    }
    return vRet;
  }
  int DFS(vector<vector<std::pair<int, int>>>& neiBo, int cur, int par,int dis)
  {
    int iRet = (0 ==dis % m_iSignalSpeed);
    for (const auto& [next,len] : neiBo[cur])
    {
      if (next == par)
      {
        continue;
      }
      iRet +=DFS(neiBo, next, cur,dis+len);
    }
    return iRet;
  }
  int m_iSignalSpeed;
  int m_c;
};

割点

本解法过于复杂,除非用了提前封装好的割点扩展类,否则被使用。

class CNeiBo
{
public: 
  static vector<vector<int>> Two(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) 
  {
    vector<vector<int>>  vNeiBo(n);
    for (const auto& v : edges)
    {
      vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
      if (!bDirect)
      {
        vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
      }
    }
    return vNeiBo;
  } 
  static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
  {
    vector<vector<std::pair<int, int>>> vNeiBo(n);
    for (const auto& v : edges)
    {
      vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
      if (!bDirect)
      {
        vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
      }
    }
    return vNeiBo;
  }
};
class CUnionFind
{
public:
  CUnionFind(int iSize) :m_vNodeToRegion(iSize)
  {
    for (int i = 0; i < iSize; i++)
    {
      m_vNodeToRegion[i] = i;
    }
    m_iConnetRegionCount = iSize;
  } 
  CUnionFind(vector<vector<int>>& vNeiBo):CUnionFind(vNeiBo.size())
  {
    for (int i = 0; i < vNeiBo.size(); i++) {
      for (const auto& n : vNeiBo[i]) {
        Union(i, n);
      }
    }
  }
  int GetConnectRegionIndex(int iNode)
  {
    int& iConnectNO = m_vNodeToRegion[iNode];
    if (iNode == iConnectNO)
    {
      return iNode;
    }
    return iConnectNO = GetConnectRegionIndex(iConnectNO);
  }
  void Union(int iNode1, int iNode2)
  {
    const int iConnectNO1 = GetConnectRegionIndex(iNode1);
    const int iConnectNO2 = GetConnectRegionIndex(iNode2);
    if (iConnectNO1 == iConnectNO2)
    {
      return;
    }
    m_iConnetRegionCount--;
    if (iConnectNO1 > iConnectNO2)
    {
      UnionConnect(iConnectNO1, iConnectNO2);
    }
    else
    {
      UnionConnect(iConnectNO2, iConnectNO1);
    }
  }
  bool IsConnect(int iNode1, int iNode2)
  {
    return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);
  }
  int GetConnetRegionCount()const
  {
    return m_iConnetRegionCount;
  }
  vector<int> GetNodeCountOfRegion()//各联通区域的节点数量
  {
    const int iNodeSize = m_vNodeToRegion.size();
    vector<int> vRet(iNodeSize);
    for (int i = 0; i < iNodeSize; i++)
    {
      vRet[GetConnectRegionIndex(i)]++;
    }
    return vRet;
  }
  std::unordered_map<int, vector<int>> GetNodeOfRegion()
  {
    std::unordered_map<int, vector<int>> ret;
    const int iNodeSize = m_vNodeToRegion.size();
    for (int i = 0; i < iNodeSize; i++)
    {
      ret[GetConnectRegionIndex(i)].emplace_back(i);
    }
    return ret;
  }
private:
  void UnionConnect(int iFrom, int iTo)
  {
    m_vNodeToRegion[iFrom] = iTo;
  }
  vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引
  int m_iConnetRegionCount;
};
class CParents
{
public:
  CParents(vector<int>& vParent, const int iMaxLeve)
  { 
    int iBitNum = 0;
    for (; (1 << iBitNum) < iMaxLeve; iBitNum++);
    const int n = vParent.size();
    m_vParents.assign(iBitNum+1, vector<int>(n, -1));
    m_vParents[0] = vParent;
    //树上倍增
    for (int i = 1; i < m_vParents.size(); i++)
    {
      for (int j = 0; j < n; j++)
      {
        const int iPre = m_vParents[i - 1][j];
        if (-1 != iPre)
        {
          m_vParents[i][j] = m_vParents[i - 1][iPre];
        }
      }
    }
  }
  int GetParent(int iNode, int iLeve)const
  {
    int iParent = iNode;
    for (int iBit = 0; iBit < m_vParents.size(); iBit++)
    {
      if (-1 == iParent)
      {
        return iParent;
      }
      if (iLeve & (1 << iBit))
      {
        iParent = m_vParents[iBit][iParent];
      }
    }
    return iParent;
  } 
protected:
  vector<vector<int>> m_vParents;
};
class C2Parents : CParents
{
public:
  C2Parents(vector<int>& vParent, const vector<int>& vLeve) :m_vLeve(vLeve)
    , CParents(vParent,*std::max_element(vLeve.begin(), vLeve.end()))
  {   
  } 
  int GetPublicParent(int iNode1, int iNode2)const
  {
    int leve0 = m_vLeve[iNode1];
    int leve1 = m_vLeve[iNode2];
    if (leve0 < leve1)
    {
      iNode2 = GetParent(iNode2, leve1 - leve0);
      leve1 = leve0;
    }
    else
    {
      iNode1 = GetParent(iNode1, leve0 - leve1);
      leve0 = leve1;
    }
    //二分查找
    int left = -1, r = leve0;
    while (r - left > 1)
    {
      const auto mid = left + (r - left) / 2;
      const int iParent0 = GetParent(iNode1, mid);
      const int iParent1 = GetParent(iNode2, mid);
      if (iParent0 == iParent1)
      {
        r = mid;
      }
      else
      {
        left = mid;
      }
    }
    return GetParent(iNode1, r);
  }
protected:
  vector<vector<int>> m_vParents;
  const vector<int> m_vLeve;
};
class CCutPointEx
{
public:
  CCutPointEx(const vector<vector<int>>& vNeiB) : m_iSize(vNeiB.size())
  {
    m_vNodeToTime.assign(m_iSize, -1);
    m_vChildFirstEnd.resize(m_iSize);
    m_vNodeToRegion.assign(m_iSize, -1);
    m_vCut.assign(m_iSize, false);
    for (int i = 0; i < m_iSize; i++)
    {
      if (-1 != m_vNodeToTime[i])
      {
        continue;
      }
      dfs(i, -1, vNeiB);
      m_iRegionCount++;
    }
    m_vTimeToNode.resize(m_iSize);
    for (int i = 0; i < m_iSize; i++)
    {
      m_vTimeToNode[m_vNodeToTime[i]] = i;;
    }
  }
  bool Visit(int src, int dest, int iCut)const
  {
    if (m_vNodeToRegion[src] != m_vNodeToRegion[dest])
    {
      return false;//不在一个连通区域
    }
    if (!m_vCut[iCut])
    {
      return true;
    }
    const int r1 = GetCutRegion(iCut, src);
    const int r2 = GetCutRegion(iCut, dest);
    return r1 == r2;
  }
  vector<vector<int>> GetSubRegionOfCut(const int iCut)const
  {//删除iCut及和它相连的边后,iCut所在的区域会分成几个区域:父节点一个区域、各子节点一个区域
   //父节点所在区域可能为空,如果iCut所在的连通区域只有一个节点,则返回一个没有节点的区域。
    const auto& v = m_vChildFirstEnd[iCut];
    vector<vector<int>> vRet(1);      
    int j = 0;
    for (int iTime=0;iTime < m_iSize; iTime++ )
    {
      const int iNode = m_vTimeToNode[iTime];
      if ((j < v.size()) && ( iTime  >= v[j].first ))
      {
        j++;
        vRet.emplace_back();
      }
      if ((iCut != iNode) && (m_vNodeToRegion[iNode] == m_vNodeToRegion[iCut]))
      {
        if (v.size()&&(iTime >= v.back().second))
        {
          vRet[0].emplace_back(iNode);
        }
        else
        {
          vRet.back().emplace_back(iNode);
        }
      }
    }
    return vRet;
  }
protected:
  int dfs(int cur, int parent, const vector<vector<int>>& vNeiB)
  {
    auto& curTime = m_vNodeToTime[cur];
    m_vNodeToRegion[cur] = m_iRegionCount;
    curTime = m_iTime++;
    int iCutChild = 0;
    int iMinTime = curTime;
    for (const auto& next : vNeiB[cur])
    {
      if (-1 != m_vNodeToTime[next])
      {
        iMinTime = min(iMinTime, m_vNodeToTime[next]);
        continue;
      }
      int iChildBeginTime = m_iTime;
      const int iChildMinTime = dfs(next, cur, vNeiB);
      iMinTime = min(iMinTime, iChildMinTime);
      if (iChildMinTime >= curTime)
      {
        iCutChild++;
        m_vChildFirstEnd[cur].push_back({ iChildBeginTime,m_iTime });
      };
    }
    m_vCut[cur] = (iCutChild + (-1 != parent)) >= 2;
    return iMinTime;
  }
  int GetCutRegion(int iCut, int iNode)const
  {
    const auto& v = m_vChildFirstEnd[iCut];
    auto it = std::upper_bound(v.begin(), v.end(), m_vNodeToTime[iNode], [](int time, const std::pair<int, int>& pr) {return time < pr.first; });
    if (v.begin() == it)
    {
      return v.size();
    }
    --it;
    return (it->second > m_vNodeToTime[iNode]) ? (it - v.begin()) : v.size();
  }
  int m_iTime = 0;
  const int m_iSize;//时间戳
  int m_iRegionCount = 0;
  vector<int> m_vNodeToTime;//各节点到达时间,从0开始。 -1表示未处理
  vector<bool> m_vCut;//是否是割点
  vector<int> m_vNodeToRegion;//各节点所在区域
  vector<vector<pair<int, int>>> m_vChildFirstEnd;//左闭右开空间[0,m_vChildFirstEnd[0].first)和[m_vChildFirstEnd.back().second,iSize)是一个区域
  vector<int> m_vTimeToNode;
};


【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目(三)https://developer.aliyun.com/article/1478726


相关文章
|
5天前
|
监控 Linux 测试技术
LabVIEW网络服务器何使用,有哪些不同
LabVIEW网络服务器何使用,有哪些不同
14 1
|
5天前
|
运维 数据安全/隐私保护
Finalshell 私钥连接服务器
Finalshell 私钥连接服务器
27 0
|
3天前
|
SQL 存储 数据库
两个Python脚本轻松解决ETL工作:统计多个服务器下所有数据表信息
两个Python脚本轻松解决ETL工作:统计多个服务器下所有数据表信息
|
3天前
|
SQL 存储 数据库
两个Python脚本轻松解决ETL工作:统计多个服务器下所有数据表信息(1)
两个Python脚本轻松解决ETL工作:统计多个服务器下所有数据表信息(1)
|
5天前
|
网络协议
LabVIEW中如何在网络上使用远程VI服务器
LabVIEW中如何在网络上使用远程VI服务器
10 2
|
5天前
|
Oracle Java 关系型数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
【服务器】python通过JDBC连接到位于Linux远程服务器上的Oracle数据库
17 6
|
5天前
|
网络协议 Dubbo Java
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
【网络编程】理解客户端和服务器并使用Java提供的api实现回显服务器
11 0
|
5天前
|
机器学习/深度学习 数据可视化 算法
R语言神经网络与决策树的银行顾客信用评估模型对比可视化研究
R语言神经网络与决策树的银行顾客信用评估模型对比可视化研究
|
5天前
|
机器学习/深度学习 数据可视化 算法
SPSS Modeler决策树和神经网络模型对淘宝店铺服装销量数据预测可视化|数据分享
SPSS Modeler决策树和神经网络模型对淘宝店铺服装销量数据预测可视化|数据分享
|
5天前
|
机器学习/深度学习 数据可视化 数据挖掘
R语言软件对房屋价格预测:回归、LASSO、决策树、随机森林、GBM、神经网络和SVM可视化|数据分享
R语言软件对房屋价格预测:回归、LASSO、决策树、随机森林、GBM、神经网络和SVM可视化|数据分享

热门文章

最新文章