NOIp 图论算法专题总结 (2)

系列索引:

(本篇未完,先坑着)

树链剖分

https://oi-wiki.org/graph/heavy-light-decomposition/

  • qRange:将树从 \(x\)\(y\) 结点最短路径上所有节点的值都加上 \(val\)
  • updRange:求树从 \(x\)\(y\) 结点最短路径上所有节点的值之和
  • qSon:将以 \(x\) 为根节点的子树内所有节点值都加上 \(val\)
  • updSon:求以 \(x\) 为根节点的子树内所有节点值之和

时间复杂度 \(O(n\log^2n)\)

int w[N], wt[N];
int t[N<<2], laz[N<<2];
int son[N], id[N], fa[N], dep[N], siz[N], top[N];

inline void pushdown(int k, int len) {
    laz[k<<1]+=laz[k];
    laz[k<<1|1]+=laz[k];
    t[k<<1] = (t[k<<1] + laz[k]*(len-(len>>1))) % MOD;
    t[k<<1|1] = (t[k<<1|1] + laz[k]*(len>>1)) % MOD;
    laz[k]=0;
}
void build(int k, int l, int r) {
    if (l==r) {t[k] = wt[l] % MOD; return; }
    int mid=(l+r)>>1;
    build(k<<1, l, mid);
    build(k<<1|1, mid+1, r);
    t[k] = (t[k<<1] + t[k<<1|1]) % MOD;
}
int query(int k, int l, int r, int x, int y) {
    if (x<=l && r<=y) {return t[k]; }
    if (laz[k]) pushdown(k, r-l+1);
    int mid=(l+r)>>1, res=0;
    if (x<=mid) res = (res + query(k<<1, l, mid, x, y)) % MOD;
    if (y>mid) res = (res + query(k<<1|1, mid+1, r, x, y)) % MOD;
    return res;
}
void update(int k, int l, int r, int x, int y, int val) {
    if (x<=l && r<=y) {laz[k]+=val, t[k]+=val*(r-l+1); return; }
    if (laz[k]) pushdown(k, r-l+1);
    int mid=(l+r)>>1;
    if (x<=mid) update(k<<1, l, mid, x, y, val);
    if (y>mid) update(k<<1|1, mid+1, r, x, y, val);
    t[k] = (t[k<<1] + t[k<<1|1]) % MOD;
}

inline int qRange(int x, int y) {
    int ans=0;
    while (top[x]!=top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        ans = (ans + query(1, 1, n, id[top[x]], id[x])) % MOD;
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x, y);
    ans = (ans + query(1, 1, n, id[x], id[y])) % MOD;
    return ans;
}
inline void updRange(int x, int y, int val) {
    val %= MOD;
    while (top[x]!=top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        update(1, 1, n, id[top[x]], id[x], val);
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x, y);
    update(1, 1, n, id[x], id[y], val);
}
inline int qSon(int x) {
    return query(1, 1, n, id[x], id[x]+siz[x]-1);
}
inline void updSon(int x, int val) {
    update(1, 1, n, id[x], id[x]+siz[x]-1, val);
}

void dfs1(int x, int f, int d) {
    dep[x] = d, fa[x] = f, siz[x] = 1;
    int heavy=-1;
    for (rint i=head[x]; i; i=nex[i]) {
        int &y=to[i]; if (y==f) continue;
        dfs1(y, x, d+1);
        siz[x]+=siz[y];
        if (siz[y]>heavy) son[x]=y, heavy=siz[y];
    }
}
void dfs2(int x, int tp) {
    id[x]=++id[0], wt[id[0]]=w[x], top[x]=tp;
    if (!son[x]) return;
    dfs2(son[x], tp);  // heavy son first
    for (rint i=head[x]; i; i=nex[i]) {
        int &y=to[i]; if (y==fa[x] || y==son[x]) continue;
        dfs2(y, y); // light son with new chain
    }
}

dfs1(root, 0, 1);
dfs2(root, root);
build(1, 1, n);


求树的重心

https://oi-wiki.org/graph/tree-misc/

拓扑排序

顶点活动网 (Activity On Vertex network, AOV) 中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列 (Topological order),由 AOV 网构造拓扑序列的过程叫做拓扑排序 (Topological sort)。AOV 网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

Kahn 算法

将入度为 0 的边组成一个集合 \(S\),每次从 \(S\) 里面取出一个顶点 \(v\) (可以随便取) 放入 \(L\),然后遍历顶点 \(v\) 的所有边 \((u_1, v), (u_2, v), (u_3, v) \cdots\) 并删除,判断如果该边的另一个顶点在移除这一条边后入度为 0,那么就将这个顶点放入集合 \(L\) 中。

不断地重复取出顶点。

最后当集合为空后,就检查图中是否存在任何边。如果有,那么这个图一定有环路,否则返回 \(L\)\(L\) 中顺序就是拓扑排序的结果。

常用队列实现 \(S\) 集合。时间复杂度 \(O(V+E )\)

int ind[N], topo[N], cnt;
queue<int> q;

for (int i=1, a, b; i<=m; i++)
    scanf("%d%d", &a, &b), add(a, b), ++ind[b];
for (int i=1; i<=n; i++) if (!ind[i]) q.push(i);
while (!q.empty()) {
    int t = q.top(), q.pop(); topo[++cnt] = t;
    for (int i=head[t]; i; i=nex[i]) {
        --ind[to[i]];
        if (!ind[to[i]]) q.push(to[i]);
    }
}
if (cnt < n) printf("有环!")


强连通分量

强连通分量(strongly connected components):有向非强连通图的极大强连通子图。

\(G=(V, E)\) 是一个极大强连通子图,当且仅当 \(G\) 是强连通子图,且不存在 \(G'=(V', E')\),使得 \(G\subsetneq G'\)

https://oi-wiki.org/graph/scc/

Tarjan 算法

Kosaraju 算法:

Kosaraju 算法依靠两次简单的 DFS 实现。

第一次 DFS,选取任意顶点作为起点,遍历所有未访问过的顶点,并在回溯之前给顶点编号,也就是后序遍历。

第二次 DFS,对于反向后的图,以标号最大的顶点作为起点开始 DFS。这样遍历到的顶点集合就是一个强连通分量。对于所有未访问过的结点,选取标号最大的,重复上述过程。

两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为 O(n+m)。


双连通分量

https://oi-wiki.org/graph/bcc/

割点和桥

https://oi-wiki.org/graph/bridge/

杂项

https://www.luogu.org/blog/chengni5673/tu-lun-di-xiao-ji-qiao-yi-ji-kuo-zhan

转载于:https://www.cnblogs.com/greyqz/p/graph2.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值