专题总结(图论)

https://www.zybuluo.com/ysner/note/1226610

最短路

(method 1)

判负环有个比较快的方法:记录经过当前点的当前最短路被更新了多少次,如果超过(n)次就确认有负环。

queue<int>Q;
il int SPFA()
{
  while(!Q.empty()) Q.pop();
  fp(i,1,n) dis[i]=0,vis[i]=1,f[i]=0,Q.push(i);
  while(!Q.empty())
    {
      re int u=Q.front();Q.pop();
      for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(dis[v]>dis[u]+e[i].w)
        {
          dis[v]=dis[u]+e[i].w;f[v]=f[u]+1;//记录被更新多少次
          if(f[v]>=n+1) return 1;
          if(!vis[v]) vis[v]=1,Q.push(v);
        }
    }
      vis[u]=0;
    }
  return 0;
}

(method 2)

给你一条数轴和(m)条线段,第(i)条线段覆盖区间([L_i,R_i]),选择它需要代价(C_i)。请选出代价和最小的一组线段使得区间([L,R])中的每一段都被覆盖。

解析:
这种问题不需要(DP)
可以从(L_i)(R_i)连权值为(C_i)的有向边,再从(x)(x-1)连权值为(0)的有向边(因为可以重复覆盖),再跑最短路即可。

(exercise)

  • [X] [vijos 1404]遭遇战
  • [X] [51nod 1433]路径和树(最小路径树)
  • [ ] [NOIP2013]华容道

差分约束

(n)个未知量,对(m)对未知量(x,y),有要求(x-yleq mx)(x-ygeq mn),如何建边?

解析:
若题目中询问两个量的最大差值,
则建边意义应为:(x-ygeq mn),则建边(x->y),权值为(mn)
(x-yleq mx)怎么处理?
两边同时取反,得(y-xgeq -mx),则建边(y->x),权值为(-mx)即可。

(exercise)

  • [X] [CJOJ P1098 奶牛排队]

最小生成树

(method 1)

(Boruvka)算法:
每次对于每个连通块找一条连接这个连通块和另外某个连通块的最小边,加入这些边。可以发现每次连通块的数目都至少会/2,因此时间复杂度O(nlog(n)*找出所求的边的复杂度)。

这个玩意一般与位运算结合。
给个例子:[[CF888G]Xor-MST][1]

(exercise)

  • [X] [BZOJ4883]棋盘上的守卫(环套树)
  • [X] [BZOJ3714]Kuglarz

二分图

(method 1)

没有奇环的图叫二分图。

(method 2)

判断二分图的方法:黑白染色。
如果有奇环,会发现某个已走过的点将染上不同的颜色。

(method 3)

匈牙利算法。
一个匹配增广的过程。
核心思想:在原来匹配完的点还能匹配到别的点的前提下,让现在的点匹配其对偶。

il int dfs(re int u)
{
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(!vis[v])
    {
      vis[v]=1;
      if(!link[v]||dfs(link[v])) {link[v]=u;return 1;}
    }
    }
  return 0;
}
fp(i,1,n)
    {
      memset(vis,0,sizeof(vis));
      ans+=dfs(i);
    }

(method 4)

(noip2010)关押罪犯
方法不少:

  • 二分答案+判断二分图
  • 并差集补集思想

其实就是一个罪犯应该出现在另一个罪犯的补集中。
每次并查集将两者并入对方的补集。
如果发现两者在同一补集中就凉了。

  • 动态二分图

从大到小加入每一条边,想办法快速判断这张图是否是二分图。在这里介绍一种应用较为广泛的方法:带权并查集。
(f[x])表示从(x)到并查集的根节点的路径的长度的奇偶性。
对于一条新边((x,y,z)),若(x,y)不在同一联通块,直接连;
否则如果(x,y)间路径长度为偶数,就凉了。
这个可以通过跳并差集父亲来算。

(exercise)

  • [X] [ZJOI2007]矩阵游戏

(Tarjan)系列

(method 1)

割点模板。没有(vis),同时注意特判当前根结点。

il void Tarjan(re int u)
{
  dfn[u]=low[u]=++tot;
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(!dfn[v])
    {
      Tarjan(v);
      low[u]=min(low[u],low[v]);
      if(low[v]>=dfn[u])
        if(u^rt) cut[u]=1;else ++gu;
    }
      else low[u]=min(low[u],dfn[v]);
    }
  if(u==rt&&gu>1) cut[u]=1;
}
int main()
{
  memset(h,-1,sizeof(h));
  n=gi();m=gi();
  fp(i,1,m)
    {
      re int u=gi(),v=gi();
      add(u,v);add(v,u);
    }
  fp(i,1,n) if(!dfn[i]) gu=0,Tarjan(rt=i);
  tot=0;
  fp(i,1,n) if(cut[i]) ++tot;
  printf("%d
",tot);
  fp(i,1,n) if(cut[i]) printf("%d ",i);puts("");
  return 0;
}

(exercise)

原文地址:https://www.cnblogs.com/yanshannan/p/9700751.html