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

系列索引:

树链剖分

  • 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 网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。时间复杂度 \(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'\)


猜你喜欢

转载自www.cnblogs.com/greyqz/p/9545927.html
今日推荐