dls的数据结构—倍增,dfs序,欧拉序


倍增

1.求lca
2.log求k级祖先
3.lca:可以记录路径上的信息(没有修改的) 点上的和边上的都可以
并且这个信息需要容易使用倍增的形式进行合并(轻量级数据合并)
// 维护路径上边的最小值
#include

using namespace std;
const int N = 2e5+10;
int h[N], ne[2 * N], e[2 * N], w[2 * N], idx;
int n, q;

void add(int a, int b, int c){
    w[idx] = c, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

const int LOGN = 20;
int depth[N], p[LOGN][N], val[LOGN][N];
void dfs(int u, int fa){
    depth[u] = depth[fa] + 1;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs(j, u);
        p[0][j] = u;
        val[0][j] = w[i];
    }
}

void init(){
    for(int i = 1; i < LOGN; i ++){
        for(int j = 1; j <= n; j ++){
            p[i][j] = p[i - 1][p[i - 1][j]];
            val[i][j] = min(val[i - 1][j], val[i - 1][p[i - 1][j]]);
        }
    }
}

int query(int u, int v){
    int res = 1 << 30;
    if(depth[u] < depth[v]) swap(u, v);
    int d = (depth[u] - depth[v]);
    for(int i = 0; i < LOGN; i ++){
        if(d >> i & 1){
            res = min(res, val[i][u]);
            u = p[i][u];
        }
    }
    if(u == v) return res;

    for(int i = LOGN - 1; i >= 0; i --){
        if(p[i][u] != p[i][v]){
            res = min(res, min(val[i][u], val[i][v]));
            u = p[i][u]; v = p[i][v];
        }
    }

    res = min(res, min(val[0][v], val[0][u]));
    return res;

}



int main(){
    scanf("%d %d", &n, &q);
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n - 1; i ++){
        int a, b, c; scanf("%d %d %d", &a, &b, &c);
        add(a, b, c); add(b, a, c);
    }
    dfs(1, 0);

    init();

    while(q --){
        int u, v; scanf("%d %d", &u, &v);
        printf("%d\n", query(u, v));
    }
}

DFS序

性质:子树标号连续
可以把子树问题转化成序列的区间问题

// 子树的点权和(区间和问题),某一个点到根的路径的权值和(每个子树加上根节点的值)
#include

using namespace std;
const int N = 2e5+10;
typedef long long LL;
int h[N], ne[2 * N], e[2 * N], w[N], idx;
LL tr1[N], tr2[N];
int l[N], r[N], tot;
int n, q;

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa){
    l[u] = ++tot;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs(j, u);
    }
    r[u] = tot;
}

int lowbit(int x){
    return x & -x;
}

void modify(LL tr[], int u, int x){
    for(int i = u; i <= n; i += lowbit(i)) tr[i] += x; 
}

LL sum(LL tr[], int x){
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

void init(){
    for(int i = 1; i <= n; i ++){
        modify(tr1, l[i], w[i]);
        modify(tr2, l[i], w[i]);
        modify(tr2, r[i] + 1, -w[i]);
    }
}

int main(){
    scanf("%d %d", &n, &q);
    memset(h, -1, sizeof h);
    
    for(int i = 1; i <= n - 1; i ++){
        int a, b; scanf("%d %d", &a, &b);
        add(a, b); add(b, a);
    }
    for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
    dfs(1, 0);

    init();

    while(q --){
        int type; scanf("%d", &type);
        if(type == 1){
            int x, y; scanf("%d %d", &x, &y);
            int d = y - w[x]; w[x] = y;
            modify(tr1, l[x], d);
            modify(tr2, l[x], d);
            modify(tr2, r[x] + 1, -d);
        }
        else if(type  == 2){
            int x; scanf("%d", &x);
            printf("%lld %lld\n", sum(tr1, r[x]) - sum(tr1, l[x] - 1), sum(tr2, l[x]));
        }
    }
}

#include

using namespace std;
const int N = 2e5+10;
typedef long long LL;
typedef pair PII;
int h[N], ne[2 * N], e[2 * N], w[N], idx;
LL tr[N];
int l[N], r[N], tot;
int n, q;
vector son[N];

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa){
    l[u] = ++tot;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs(j, u);
        son[u].push_back({l[j], r[j]});
    }
    r[u] = tot;
}

int lowbit(int x){
    return x & -x;
}

void modify(int u, int x){
    for(int i = u; i <= n; i += lowbit(i)) tr[i] += x; 
}

LL sum(int x){
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

void init(){
    for(int i = 1; i <= n; i ++){
        modify(l[i], w[i]);
    }
}

int main(){
    scanf("%d %d", &n, &q);
    memset(h, -1, sizeof h);
    
    for(int i = 1; i <= n - 1; i ++){
        int a, b; scanf("%d %d", &a, &b);
        add(a, b); add(b, a);
    }
    for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
    dfs(1, 0);

    init();
    int root = 1;
    while(q --){
        int type; scanf("%d", &type);
        if(type == 1){
            int x, y; scanf("%d %d", &x, &y);
            int d = y - w[x]; w[x] = y;
            modify(l[x], d);
        }
        else if(type  == 2){
            int x; scanf("%d", &x);
            if(x == root) printf("%lld\n", sum(n));
            else if(l[x] < l[root] && r[x] >= r[root]){
                auto seg = *prev(upper_bound(son[x].begin(), son[x].end(), (PII){l[root], r[root]}));
                printf("%lld\n", sum(n) - (sum(seg.second) - sum(seg.first - 1)));
            }
            else{
                printf("%lld\n", sum(r[x]) - sum(l[x] - 1) );
            }

            
        }
        else if(type == 3) scanf("%d", &root);
    }
}
例如求解nlogn次两个点之间的距离:depth[u] + depth[v] - 2 * depth[lca]
可以使用欧拉序让lca变成log
欧拉序:dfs每次访问到的点都记录下来
u和v的lca一定是,uv在欧拉序里面之间深度最小的点,所以变成了rmq问题了,用st表搞定
括号序列:每个点访问入栈的时候左括号,出栈的时候右括号


const int LOGN = 20;
// 欧拉序的长度要开两倍
PII f[LOGN + 2][2 * N];
int l[N], r[N], depth[N], tot;
// 求欧拉序列,访问的时候加入序列,访问完一个儿子回来的时候加入序列
void dfs(int u, int fa){
    l[u] = ++tot;
    depth[u] = depth[fa] + 1;
    f[0][tot] = {depth[u], u};
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs(j, u);
        f[0][++tot] = {depth[u], u};
    }
    r[u] = tot;
}
// nlogn预处理出来lca
void init(){
    for(int i = 1; i <= LOGN; i ++){
        for(int j = 1; j + (1 << i) - 1 <= tot; j ++){
            f[i][j] = min(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
        }
    }
}
// 求L和R的lca
if(L > R) swap(L, R);
int len = __lg(R - L + 1);
int lca = min(f[len][L], f[len][R - (1 << len) + 1]).second;