【树上倍增】【割点】 【换根法】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


相关文章
|
22天前
|
缓存 应用服务中间件 nginx
Web服务器的缓存机制与内容分发网络(CDN)
【8月更文第28天】随着互联网应用的发展,用户对网站响应速度的要求越来越高。为了提升用户体验,Web服务器通常会采用多种技术手段来优化页面加载速度,其中最重要的两种技术就是缓存机制和内容分发网络(CDN)。本文将深入探讨这两种技术的工作原理及其实现方法,并通过具体的代码示例加以说明。
58 1
|
21天前
|
监控 安全 数据挖掘
网络游戏服务器如何有效防护DDoS与CC攻击
随着网络游戏行业的蓬勃发展,其背后的服务器架构日益复杂,同时也面临着前所未有的网络安全威胁。其中,分布式拒绝服务(DDoS)和CC(Challenge Collapsar,一种针对网页的攻击方式)攻击尤为突出,它们通过大量伪造请求或恶意流量,使服务器资源耗尽,导致服务中断或响应缓慢。因此,保障网络游戏服务器的安全,有效防护DDoS与CC攻击,已成为游戏行业亟待解决的问题。
|
2天前
|
存储 弹性计算 测试技术
阿里云服务器实例规格vCPU、内存、网络带宽、网络收发包PPS、连接数等性能指标详解
阿里云服务器ECS实例可以分为多种实例规格族。根据CPU、内存等配置,一种实例规格族又分为多种实例规格。而实例规格又包含vCPU、处理器、内存、vTPM、本地存储、网络带宽、网络收发包PPS、连接数、弹性网卡、云盘带宽、云盘IOPS等指标,本文为大家详细介绍实例规格的这些指标,以供大家了解和选择。
阿里云服务器实例规格vCPU、内存、网络带宽、网络收发包PPS、连接数等性能指标详解
|
7天前
|
存储 运维 网络协议
运维的基本概念:服务器和网络基础知识
运维的基本概念:服务器和网络基础知识
19 0
运维的基本概念:服务器和网络基础知识
|
24天前
|
数据可视化 Ubuntu Linux
PyCharm连接远程服务器配置的全过程
相信很多人都遇见过这种情况:实验室成员使用同一台服务器,每个人拥有自己的独立账号,我们可以使用服务器更好的配置完成实验,毕竟自己哪有money拥有自己的3090呢。 通常服务器系统采用Linux,而我们平常使用频繁的是Windows系统,二者在操作方面存在很大的区别,比如我们实验室的服务器采用Ubuntu系统,创建远程交互任务时可以使用Terminal终端或者VNC桌面化操作,我觉得VNC很麻烦,所以采用Terminal进行实验,但是Terminal操作给我最不好的体验就是无法可视化中间实验结果,而且实验前后的数据上传和下载工作也让我头疼不已。
|
30天前
|
网络协议 Linux
在Linux中,如何分析网络连接和端口占用情况?
在Linux中,如何分析网络连接和端口占用情况?
|
19天前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
42 0
|
21天前
|
网络安全 数据安全/隐私保护
VSC通过 SSH 连接到远程服务器时,每次都需要输入密码
VSC通过 SSH 连接到远程服务器时,每次都需要输入密码
110 0
|
22天前
|
Linux 网络安全 网络架构
如何处理在学校Linux连接不上服务器
如何处理在学校Linux连接不上服务器
34 0
|
26天前
|
网络协议