图论笔记
最短路
Floyd
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
P13519
在一个边权均为0的无向图中每次将一条边边权变为1
求所有点到其他点最短路之和
1.可离线处理时加边减边操作可互换。因为最短路松弛后结果不增,可以考虑将减边转化为加边。
2.只改变一条边边权时只用重新用这条边松弛一遍,不用\(N^3\)写法
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],min(f[i][x]+f[y][j],f[i][y]+f[x][j]));
dijstra
结构体写法
struct nod
{
int x,w;
bool operator <(nod other)const//默认大根堆是用'<'实现的 所以重载的是'<'
{
return w>other.w;//将'<'重载为'>'
}
};
主体
q.push({st,0});
dis[st]=0;
while(!q.empty())
{
nod top=q.top();
q.pop();
if(vis[top.x]) continue;
vis[top.x]=1;
for(auto to:g[top.x])
{
if(dis[to.x]>to.x+top.x)
dis[to.x]=to.x+top.x,q.push({to.x,dis[to.x]});
}
}
对比SPFA
while (!q.empty()) {
int u = q.front();
q.pop(), vis[u] = 0;
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1; // 记录最短路经过的边数
if (cnt[v] >= n) return false;
// 在不经过负环的情况下,最短路至多经过 n - 1 条边
// 因此如果经过了多于 n 条边,一定说明经过了负环
if (!vis[v]) q.push(v), vis[v] = 1;
}
}
}
只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作。因此队列中所存元素是所有待松弛的点。
dij中每个点最多被松弛一次,SPFA中vis数组记录的则是队列中元素。
P1462
有一个带权无向图,求满足路径长度\(dis\leq b\)时路径上点权最大值的最小值。
最大值最小/最小值最大问题 考虑二分
对点权二分答案跑dij。
Tarjan
void Scc(int x)
{
s.push(x);
is[x]=1;
dfn[x]=low[x]=++tot;
for(auto to:g[x])
{
if(!dfn[x]) Scc(to),low[x]=min(low[x],low[to]);
if(is[x]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x])
{
tot_scc++;
while(1)
{
int top=s.top();
s.pop();
is[top]=0;
if(top==x) break;
}
}
}
树剖
一条重链的起始是重儿子的父节点。
初始化两次dfs。
void dfs1(int x,int father)
{
fa[x]=father,dep[x]=dep[father]+1,sz[x]=1;
for(auto to:g[x])
{
if(to!=father) dfs1(to,x);
sz[x]+=sz[to];
if(sz[to]>sz[son[x]]) son[x]=to;
}
}
void dfs2(int x,int ftop)
{
top[x]=ftop,dfn[x]=++tot,id[tot]=x;
if(son[x]) dfs2(son[x],ftop);
for(auto to:g[x])
if(to!=son[x]&&to!=fa[x]) dfs2(to,to);
}
重链剖分求LCA
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];//注意这里跳到链头是ftop
}
if(dep[x]<dep[y]) return x;
else return y;
}
最小生成树
Kruskal贪心加边。
复习一下并查集。
路径压缩
//查找
int find(int x)
{
if(a[x]==x) return x;
else return a[x]=find(a[x]);
}
//合并
void Merge(int x,int y)
{
a[find(x)]=a[find(y)];
}
启发式合并:将节点较少的树连到另一棵。
//初始化
void Init()
{
for(int i=1;i<=n;i++) sz[i]=1,pa[i]=i;
}
//查找
int find(int x)
{
if(pa[x]==x) return x;
else return a[x];
}
//合并
void merge(int xx,int yy)
{
int x=find(xx),y=find(yy);
if(x==y) return ;
if(sz[x]<sz[y]) swap(x,y);
pa[y]=x;
sz[x]+=sz[y];
}

浙公网安备 33010602011771号