首页 > 其他分享 >【转载】毛毛虫剖分

【转载】毛毛虫剖分

时间:2022-10-22 20:47:13浏览次数:78  
标签:剖分 int top son 100005 dep dfn 毛毛虫 转载

转载声明

转载自关于毛毛虫剖分 - 一粒夸克

定义

一种由重链剖分推广而成的树上结点重标号方法,支持 修改/查询 一只毛毛虫的信息,并且可以对毛毛虫的身体和足分别 修改/查询 不同信息 。

可以用来解决一些大力树剖也可以解决的问题。

一些定义:

毛毛虫:一条树上的链和与这条链邻接的所有结点构成的集合;
虫身:毛毛虫的链部分;
虫足:毛毛虫除虫身的部分。

重标号方法:

首先重剖求出重链。若现在递归处理到结点 u:

若 u 还未被标号,则为其标号;

若 u 是链头,遍历这条重链,将邻接这条链的结点依次标号;

先递归重儿子,再递归轻儿子。

重标号性质:

对于重链,除链头外的结点标号连续;

对于任意结点,其轻儿子标号连续;

对于以重链头为根的子树,与这条重链邻接的所有结点标号连续;

借此我们可以很容易地求维护每条毛毛虫的信息。

同时也能顺便维护重链链分的所有信息以及子树的所有信息(一棵子树至多剖分为三个不交区间:重链区间、邻接轻点区间、邻接轻子树区间),复杂度与重链剖分完全一样。

例题:

NOI2021 轻重边

做法:对原树进行毛毛虫剖分后,要支持对一条毛毛虫身体染黑色,足部染白色,查询一条链上的黑点数量。

#include <bits/stdc++.h>
using namespace std;
int T;
int n, m;
int ver[200005], ne[200005], head[100005], tot;
inline void link(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}
int siz[100005], son[100005], fa[100005], dep[100005];
void dfs1(int x, int fi) {
    siz[x] = 1; fa[x] = fi; dep[x] = dep[fi] + 1;
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fi) continue;
        dfs1(u, x); siz[x] += siz[u];
        if (siz[u] > siz[son[x]]) son[x] = u;
    }
}
int top[100005];
int dfn[100005], cnt, lein[100005], leout[100005], alout[100005], subin[100005], subout[100005];
void cover(int x) {
    lein[x] = cnt + 1;
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fa[x] || u == son[x]) continue;
        dfn[u] = ++cnt;
    }
    leout[x] = cnt;
    if (son[x]) cover(son[x]);
    alout[x] = cnt;
}
void dfs2(int x, int fi) {
    top[x] = fi;
    if (!dfn[x]) dfn[x] = ++cnt;
    if (x == top[x]) cover(x);
    subin[x] = cnt + 1;
    if (son[x]) dfs2(son[x], fi);
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fa[x] || u == son[x]) continue;
        dfs2(u, u);
    }
    subout[x] = cnt;
}
int tree[400005], lazy[400005];
void build(int l = 1, int r = n, int i = 1) {
    tree[i] = 0; lazy[i] = -1;
    if (l == r) return ;
    int mid = (l + r) >> 1;
    build(l, mid, i << 1);
    build(mid + 1, r, i << 1 | 1);
}
inline void push(int i, int l, int r) {
    int mid = (l + r) >> 1;
    tree[i << 1] = lazy[i] * (mid - l + 1); lazy[i << 1] = lazy[i];
    tree[i << 1 | 1] = lazy[i] * (r - mid); lazy[i << 1 | 1] = lazy[i];
    lazy[i] = -1;
}
void update(int fr, int to, int v, int l = 1, int r = n, int i = 1) {
    if (fr > r || to < l) return ;
    if (fr <= l && to >= r) {
        tree[i] = (r - l + 1) * v; lazy[i] = v;
        return ;
    }
    if (~lazy[i]) push(i, l, r);
    int mid = (l + r) >> 1;
    update(fr, to, v, l, mid, i << 1); update(fr, to, v, mid + 1, r, i << 1 | 1);
    tree[i] = tree[i << 1] + tree[i << 1 | 1];
}
inline void lca(int x, int y) {
    vector<int> vec;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        update(lein[top[x]], leout[x], 0);
        update(dfn[son[x]], dfn[son[x]], 0);
        if (x != top[x]) update(dfn[son[top[x]]], dfn[x], 1);
        vec.push_back(top[x]);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    update(lein[y], leout[x], 0);
    update(dfn[y], dfn[y], 0);
    update(dfn[son[x]], dfn[son[x]], 0);
    if (x != y) update(dfn[son[y]], dfn[x], 1);
    for (auto it : vec) update(dfn[it], dfn[it], 1);
}
int query(int fr, int to, int l = 1, int r = n, int i = 1) {
    if (fr > r || to < l) return 0;
    if (fr <= l && to >= r) return tree[i];
    if (~lazy[i]) push(i, l, r);
    int mid = (l + r) >> 1;
    return query(fr, to, l, mid, i << 1) + query(fr, to, mid + 1, r, i << 1 | 1);
}
inline int Query(int x, int y) {
    int res = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        if (top[x] != x) res += query(dfn[son[top[x]]], dfn[x]);
        res += query(dfn[top[x]], dfn[top[x]]);
        x = fa[top[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    if (x != y) res += query(dfn[son[y]], dfn[x]);
    return res;
}
inline void clear() {
    for (int i = 1; i <= n; i++) head[i] = 0;
    tot = 0;
    for (int i = 1; i <= n; i++) siz[i] = 0;
    for (int i = 1; i <= n; i++) son[i] = 0;
    for (int i = 1; i <= n; i++) dfn[i] = 0;
    cnt = 0;
    build();
}
inline void solve() {
    scanf("%d%d", &n, &m);
    clear();
    for (int i = 1; i < n; i++) {
        int x, y; scanf("%d%d", &x, &y);
        link(x, y); link(y, x);
    }
    dfs1(1, 1);
    dfs2(1, 1);
    while (m--) {
        int op, x, y; scanf("%d%d%d", &op, &x, &y);
        if (op == 1) lca(x, y);
        else printf("%d\n", Query(x, y));
    }

}
int main() {
    freopen("edge.in", "r", stdin);
    freopen("edge.out", "w", stdout);
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

基础树剖练习题

题目:给定一棵 n 个点以 1 为根的有根树,边有颜色,初始时全为白色。

接下来有三种操作:

1 u 表示修改 u 到根路径上的边的颜色,具体修改方式在下文说明。
2 u 表示查询 u 到根路径上的黑边的个数。
3 u 表示查询 u 子树内黑边的个数。
修改方式:首先,将从 u 到根的链拉出来,即为 lis1,lis2,...,lism,其中 lisi=u,lism=1,∀1≤i<m lisi+1=falsti。

然后,除去 u 到根路径上的边,将其余与 lis1,lis3,lis5,... 相连的边染成黑色,将与 lis2,lis4,lis6,... 相连的边染成白色。

最后,对于在 u 到根路径上的边,将 (lis1,lis2),(lis3,lis4),(lis5,lis6),... 染成黑色, 将 (lis2,lis3),(lis4,lis5),(lis6,lis7),... 染成白色。

做法:对原树进行剖分后,对于 1 操作,我们要对毛毛虫身体上与 x 深度奇偶性相同的点染黑,不同的染白,足部奇偶性不同的点染黑,相同的染白,2,3 操作查询在链上和子树内的黑点个数即可。

#include <bits/stdc++.h>
using namespace std;
int n, q;
int ver[200005], ne[200005], head[100005], tot;
inline void link(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}
int siz[100005], son[100005], fa[100005], dep[100005];
void dfs1(int x, int fi) {
    siz[x] = 1;
    dep[x] = dep[fi] + 1;
    fa[x] = fi;
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fi) continue;
        dfs1(u, x); siz[x] += siz[u];
        if (siz[u] > siz[son[x]]) son[x] = u;
    }
}
int dfn[100005], cnt, top[100005];
int lein[100005], leout[100005], subin[100005], subout[100005], alout[100005];
void cover(int x) {
    lein[x] = cnt + 1;
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fa[x] || u == son[x]) continue;
        dfn[u] = ++cnt;
    }
    leout[x] = cnt;
    if (son[x]) cover(son[x]);
    alout[x] = cnt;
}
void dfs2(int x, int fi) {
    top[x] = fi;
    if (!dfn[x]) dfn[x] = ++cnt;
    if (x == top[x]) cover(x);
    subin[x] = cnt + 1;
    if (son[x]) dfs2(son[x], fi);
    for (int i = head[x]; i; i = ne[i]) {
        int u = ver[i];
        if (u == fa[x] || u == son[x]) continue;
        dfs2(u, u);
    }
    subout[x] = cnt;
}
int tree[3][400005], lazy[400005];
void insert(int loc, int v, int l = 1, int r = n, int i = 1) {
    if (loc < l || loc > r) return;
    lazy[i] = -1;
    if (l == r) {
        tree[v][i] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    insert(loc, v, l, mid, i << 1);
    insert(loc, v, mid + 1, r, i << 1 | 1);
    tree[0][i] = tree[0][i << 1] + tree[0][i << 1 | 1];
    tree[1][i] = tree[1][i << 1] + tree[1][i << 1 | 1];
}
inline void push(int i) {
    tree[2][i << 1] = tree[lazy[i]][i << 1];
    lazy[i << 1] = lazy[i];
    tree[2][i << 1 | 1] = tree[lazy[i]][i << 1 | 1];
    lazy[i << 1 | 1] = lazy[i];
    lazy[i] = -1;
}
void update(int fr, int to, int v, int l = 1, int r = n, int i = 1) {
    if (fr > r || to < l) return;
    if (fr <= l && to >= r) {
        tree[2][i] = tree[v][i]; lazy[i] = v;
        return;
    }
    if (~lazy[i]) push(i);
    int mid = (l + r) >> 1;
    update(fr, to, v, l, mid, i << 1);
    update(fr, to, v, mid + 1, r, i << 1 | 1);
    tree[2][i] = tree[2][i << 1] + tree[2][i << 1 | 1];
}
int query(int fr, int to, int l = 1, int r = n, int i = 1) {
    if (fr > r || to < l) return 0;
    if (fr <= l && to >= r) return tree[2][i];
    if (~lazy[i]) push(i);
    int mid = (l + r) >> 1;
    return query(fr, to, l, mid, i << 1) + query(fr, to, mid + 1, r, i << 1 | 1);
}
inline void lca(int x, int y, int v) {
    vector<int> vec;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        update(lein[top[x]], leout[x], v ^ 1);
        update(dfn[son[x]], dfn[son[x]], v ^ 1);
        if (x != top[x]) update(dfn[son[top[x]]], dfn[x], v);
        vec.push_back(top[x]); x = fa[top[x]];
    }
    if (dep[y] > dep[x]) swap(x, y);
    update(lein[y], leout[x], v ^ 1);
    update(dfn[son[x]], dfn[son[x]], v ^ 1);
    if (x != y) update(dfn[son[y]], dfn[x], v);
    for (auto it : vec) update(dfn[it], dfn[it], v);
}
inline int Query(int x, int y) {
    int res = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        if (x != top[x]) res += query(dfn[son[top[x]]], dfn[x]);
        res += query(dfn[top[x]], dfn[top[x]]); x = fa[top[x]];
    }
    if (dep[y] > dep[x]) swap(x, y);
    if (x != y) res += query(dfn[son[y]], dfn[x]);
    return res;
}
int main() {
    freopen("chain.in", "r", stdin);
    freopen("chain.out", "w", stdout);
    scanf("%d", &n);
    for (int i = 2; i <= n; i++) {
        int x; scanf("%d", &x);
        link(i, x); link(x, i);
    }
    dfs1(1, 1);
    dfs2(1, 1);
    for (int i = 2; i <= n; i++) insert(dfn[i], dep[i] & 1);
    scanf("%d", &q);
    while (q--) {
        int op, x;
        scanf("%d%d", &op, &x);
        if (op == 1)
            lca(1, x, dep[x] & 1);
        if (op == 2)
            printf("%d\n", Query(1, x));
        if (op == 3)
            printf("%d\n", query(lein[x], alout[x]) + query(subin[x], subout[x]));
    }

    return 0;
}

标签:剖分,int,top,son,100005,dep,dfn,毛毛虫,转载
From: https://www.cnblogs.com/kyeecccccc/p/16817235.html

相关文章

  • 怎么批量在单元格前面加文字(转载)
    https://jingyan.baidu.com/article/a681b0de68322e7a184346f1.html 点击单元格格式选中需要在前面加文字的单元格,比如:G3-G6,点击【单元格格式】。 自定义类型......
  • 【转载】使用Request对象获取Web获取当前请求的信息
    0.转载于:https://blog.csdn.net/weixin_34321977/article/details/863354991.Request简介Request对象是.net的内置对象之一,也是.net中常用的对象,用于获取客户端的信息,......
  • [转载] MATLAB | RGB image representation
    转载自https://www.geeksforgeeks.org/matlab-rgb-image-representation/MATLAB|RGBimagerepresentationAnRGBimagecanbeviewedasthreedifferentimages(ar......
  • [转载] Image Pixels
    转载自http://shutha.org/node/789 ImagePixelsPicturesthatareprintedorthataredisplayedonadigitalscreenlikeamonitororaresimplyindigita......
  • 回溯法 转载自carl的github
    参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!17.电话号码的字母组合力扣题目链接给定一个仅包含数字2-9的字符串,返回所有它能表示的字......
  • html项目引入element-ui和vue【转载】
    本地引用element-ui和vue<linkrel="stylesheet"type="text/css"href="./element.css"><scriptsrc="./js/vue.js"></script><scriptsrc="./js/element.js"></scrip......
  • AcCoders 10477:【省选基础数据结构 树链剖分】【GDOI2016】疯狂动物城 题解
    算法:树链剖分,可持久化线段树。题目大意给定\(n\)个结点的一棵树,\(m\)次操作,操作有三种:将\(x\)至\(y\)最短路径上的所有点的权值加上\(delta\)。对于\(x\)至......
  • jmeter的线程组设置(转载)
      1、取样器错误后要执行的动作:继续:忽略错误,继续执行StartNextThreadLoop:忽略错误,线程当前循环终止,执行下一个循环。停止线程:当前线程停止执行,不影响其他线程......
  • Java 注解【转载】
    Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标......
  • GCC 选项 “-Wl,-rpath=“ 转载文章
    -Wl,-rpath=<your_lib_dir> 为程序添加一个运行时库文件搜索路径。-Wl:这个是gcc的参数,表示编译器将后面的参数传递给链接器ld。-rpath:添加一个文件夹作为运行时库的搜索......