系列索引:

树链剖分

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("有环!")

强连通分量

在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强连通,如果任意两点都强连通,那么称这个图为强连通图;一个有向图的极大强连通子图称为强连通分量。

一个强连通分量中的点一定处于搜索树中同一棵子树中。

Tarjan 算法:

  • low[] 表示这个点以及其子孙节点连的所有点中dfn最小的值.
  • s[] 表示当前所有可能能构成是强连通分量的点.
  • col[] 对强连通分量进行染色.
  • v[to[k]]==false 说明无论如何这个点也不能与u构成强连通分量,因为它不能到达u.
  • low[x]==dfn[x] 说明u点及u点之下的所有子节点没有边是指向u的祖先的了,即u点与它的子孙节点构成了一个最大的强连通图即强连通分量.
  • if (!dfn[i]) tarjan(i); Tarjan一遍不能搜完所有的点,因为存在孤立点. 所以我们要对一趟跑下来还没有被访问到的点继续跑Tarjan.

均摊时间复杂度 \(O(n)\).

int dfn[N], low[N], t, s[N], st;
int col[N], ct;
bool v[N]; void tarjan(int x) {
dfn[x]=low[x]=++t, s[++st]=x, v[x]=true;
for (int k=head[x]; k; k=nex[k]) {
if (dfn[to[k]]) {if (v[to[k]]) low[x]=min(low[x], dfn[to[k]]); }
else tarjan(to[k]), low[x]=min(low[x], low[to[k]]);
}
if (low[x]==dfn[x]) {
col[x]=++ct, v[x]=false;
while (s[st]!=x) col[s[st]]=ct, v[s[st--]]=false;
--st;
}
} for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i);

[USACO06JAN] The Cow Prom:

for (rint i=1; i<=n; ++i) ++cnt[col[i]];
for (rint i=1; i<=ct; ++i) if (cnt[i]>1) ++ans;
printf("%d\n", ans);

缩点

缩点: 对于 贡献具有传递性 的题,因为强连通分量中的每两个点都是强连通的,可以将一个强连通分量当做一个 超级点,而点权按题意来定.

POJ2186 Popular Cows: 告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜.

显然一个强联通分量内的所有点都是满足条件的,我们可以对整张图进行缩点,然后就简单了.

剩下的所有点都不是强连通的,现在整张图就是一个DAG(有向无环图).

那么就变成一道水题了,因为这是一个有向无环图,不存在所有点的出度都不为零的情况.

所以必然有1个及以上的点出度为零,如果有两个点出度为零,那么这两个点肯定是不相连的,即这两圈牛不是互相崇拜的,于是此时答案为零,如果有1个点出度为0,那么这个点就是被全体牛崇拜的.

这个点可能是一个强联通分量缩成的 超级点,所以应该输出整个强联通分量中点的个数.

stxy-ferryman

/* 以上同 Tarjan 求强连通分量. */

int deg[N], cnt[N];
int tot=0, ans=0; for (int i=1; i<=n; ++i) {
for (int k=head[i]; k; k=nex[k]) if (col[to[k]]!=col[i]) ++deg[col[i]];
++cnt[col[i]];
}
for (int i=1; i<=ct; ++i) if (!deg[i]) ++tot, ans=cnt[i];
if (!tot || tot>1) printf("0\n"); else printf("%d\n", ans);

割点、桥

无向图.

割点

特判:根节点如果有两个及以上的儿子,那么他也是割点.

int dfn[N], low[N], t, root;
bool cut[N]; void tarjan(int x) {
int flag=0;
dfn[x]=low[x]=++t;
for (int k=head[x]; k; k=nex[k]) {
if (dfn[to[k]]) low[x]=min(low[x], dfn[to[k]]);
else {
tarjan(to[k]), low[x]=min(low[x], low[to[k]]);
if (low[y]>=dfn[x]) {
++flag;
if (x!=root || flag>1) cut[x]=true;
}
}
}
} for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(root=i);
for (int i=1; i<=n; ++i) if (cut[i]) printf("%d ", i);

邻接表存图编号从2开始. 即开头 head[0]=1;.

int dfn[N], low[N], t;
bool bdg[N<<1]; void tarjan(int x, int last) {
dfn[x]=low[x]=++t;
for (int k=head[x]; k; k=nex[k]) {
if (dfn[to[k]]) if (i!=(last^1)) low[x]=min(low[x], dfn[to[k]]);
else {
tarjan(to[k], k), low[x]=min(low[x], low[to[k]]);
if (low[y]>=dfn[x]) bdg[k]=bdg[k^1]=true;
}
}
} for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i, 0);
for (int i=2; i<head[0]; i+=2) if (bdg[i]) printf("%d %d\n", to[i^1], to[i]);

杂项

图论技巧

NOIp 图论算法专题总结 (2)的更多相关文章

  1. NOIp 图论算法专题总结 (1):最短路、最小生成树、最近公共祖先

    系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 最短路 Floyd 基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间 ...

  2. NOIp 图论算法专题总结 (3):网络流 & 二分图 简明讲义

    系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 网络流 概念 1 容量网络(capacity network)是一个有向图,图的 ...

  3. 算法专题 | 10行代码实现的最短路算法——Bellman-ford与SPFA

    今天是算法数据结构专题的第33篇文章,我们一起来聊聊最短路问题. 最短路问题也属于图论算法之一,解决的是在一张有向图当中点与点之间的最短距离问题.最短路算法有很多,比较常用的有bellman-ford ...

  4. 图论算法-最小费用最大流模板【EK;Dinic】

    图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...

  5. 图论算法-网络最大流【EK;Dinic】

    图论算法-网络最大流模板[EK;Dinic] EK模板 每次找出增广后残量网络中的最小残量增加流量 const int inf=1e9; int n,m,s,t; struct node{int v, ...

  6. 图论算法-Tarjan模板 【缩点;割顶;双连通分量】

    图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; ...

  7. NOIP基本算法

    NOIP基本算法 1.二分 poj 2018 Best Cow Fences ▪ http://poj.org/problem?id=2018 ▪ 题意:给定一个正整数数列

  8. [算法专题] LinkedList

    前段时间在看一本01年出的旧书<effective Tcp/Ip programming>,这个算法专题中断了几天,现在继续写下去. Introduction 对于单向链表(singly ...

  9. 【枚举Day1】20170529-2枚举算法专题练习 题目

    20170529-2枚举算法专题练习 题解: http://www.cnblogs.com/ljc20020730/p/6918360.html 青岛二中日期 序号 题目名称 输入文件名 输出文件名 ...

随机推荐

  1. vue-过滤器(filter)

    1.全局过滤器(项目中所有的vue文件都可以使用) 1.1  直接注册全局过滤器 在main.js中注册: 在项目中使用; 前面的为时间,作为filter过滤器的第一个参数. 1.2 所有过滤器写在一 ...

  2. Altium Designer chapter1总结

    第一章操作基础中有以下几点需要注意: (1)随着DSP.ARM.FPGA等高速逻辑元件的应用,PCB的信号完整性与抗干扰性能显得尤为重要. (2)Altium Designer的发展史:Protel ...

  3. 【Unity渲染】Camera RenderToCubemap 渲染到立方体纹理

    Unity圣典 传送门:http://www.ceeger.com/Script/Camera/Camera.RenderToCubemap.html Camera.RenderToCubemap 有 ...

  4. 高德WMTS规则

    新版 http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1& ...

  5. 好书推荐---Python网络编程基础

    Python网络编程基础详细的介绍了网络编程的相关知识,结合python,看起来觉得很顺畅!!!

  6. go 协程(Goroutine)

    Go 协程是什么? Go 协程是与其他函数或方法一起并发运行的函数或方法.Go 协程可以看作是轻量级线程.与线程相比,创建一个 Go 协程的成本很小.因此在 Go 应用中,常常会看到有数以千计的 Go ...

  7. P5459 [BJOI2016]回转寿司

    传送门 暴力怎么搞,维护前缀和 $s[i]$ ,对于每一个 $s[i]$,枚举所有 $j\in[0,i-1]$,看看 $s[i]-s[j]$ 是否属于 $[L,R]$ 如果属于就加入答案 $s[i]- ...

  8. C# DataTable、实体相互转换

    public static T GetEntity<T>(DataTable table) where T : new() { T entity = new T(); foreach (D ...

  9. 2018-8-10-如何使用-Q#

    title author date CreateTime categories 如何使用 Q# lindexi 2018-08-10 19:16:51 +0800 2018-2-13 17:23:3 ...

  10. smbumount - 为普通用户卸载smb文件系统

    总览 smbumount 装载点 描述 普通用户使用这个程序可以卸载smb文件系统.它在工作时会suid到root身份,并且向普通linux用户提供了对资源更多的控制能力.在suid方面,它拥有足够的 ...