BNUOJ39566 Do use segment tree (树链剖分+维护区间最大连续和)

本文介绍了一种结合树链剖分与线段树的数据结构算法,用于高效处理树形结构上的路径修改与查询操作。通过将树转换为线段树,可以快速更新节点权重并查询路径上节点的最大连续子序列和。

Do use segment tree

Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and output a value for each output query. The given tree is connected and each node on the tree has a weight wi (-10,000 ≤ wi ≤ 10,000).

Each query consists of a number ti (ti = 1, 2), which indicates the type of the query , and three numbers ai, bi and ci (1 ≤ ai, bi ≤ n, -10,000 ≤ ci ≤ 10,000). Depending on the query type, process one of the followings:

(ti = 1: modification query) Change the weights of all nodes on the shortest path between ai and bi (both inclusive) to ci.

(ti = 2: output query) First, create a list of weights on the shortest path between ai and bi (both inclusive) in order. After that, output the maximum sum of a non-empty continuous subsequence of the weights on the list. ci is ignored for output queries.

Input

The first line contains two integers n and q. On the second line, there are n integers which indicate w1, w2, … , wn.

Each of the following n - 1 lines consists of two integers si and ei (1 ≤ si, ei ≤ n), which means that there is an edge between si and ei.

Finally the following q lines give the list of queries, each of which contains four integers in the format described above. Queries must be processed one by one from top to bottom.

Output

For each output query, output the maximum sum in one line.

Sample Input 1

3 4
1 2 3
1 2
2 3
2 1 3 0
1 2 2 -4
2 1 3 0
2 2 2 0

Output for the Sample Input 1

6
3
-4

Sample Input 2

7 5
-8 5 5 5 5 5 5
1 2
2 3
1 4
4 5
1 6
6 7
2 3 7 0
2 5 2 0
2 4 3 0
1 1 1 -1
2 3 7 0

Output for the Sample Input 2

12
10
10
19

Sample Input 3

21 30
10 0 -10 -8 5 -5 -4 -3 1 -2 8 -1 -7 2 7 6 -9 -6 3 4 9
10 3
3 2
3 12
12 4
4 13
4 9
10 21
21 1
1 11
11 14
1 15
10 6
6 17
6 16
6 5
5 18
5 19
10 7
10 8
8 20
1 1 21 -10
1 3 19 10
2 1 13 0
1 4 18 8
1 5 17 -5
2 16 7 0
1 6 16 5
1 7 15 4
2 4 20 0
1 8 14 3
1 9 13 -1
2 9 18 0
1 10 12 2
1 11 11 -8
2 21 15 0
1 12 10 1
1 13 9 7
2 6 14 0
1 14 8 -2
1 15 7 -7
2 10 2 0
1 16 6 -6
1 17 5 9
2 12 17 0
1 18 4 6
1 19 3 -3
2 11 8 0
1 20 2 -4
1 21 1 -9
2 5 19 0

Output for the Sample Input 3

20
9
29
27
10
12
1
18
-2
-3

  • 树链剖分转化为线段树;
  • 线段树上维护最大前缀和,最大后缀和,最大区间连续和,区间和。

    剩下部分就随便搞搞。

  • 代码 36 ms 31040 KB 4521 B 2015-10-11 00:45:56

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 200000 + 10;

//tree
int head[maxn], to[maxn<<1], nxt[maxn<<1], tot = 0, sz = 0;

void addedge(int _u, int _v){
    to[++tot] = _v; nxt[tot] = head[_u]; head[_u] = tot;
}

//divide tree
int siz[maxn], fa[maxn], dep[maxn];

void dfs1(int u){
    siz[u] = 1;
    for(int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if (v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v];
    }
}
int pos[maxn], belong[maxn], szu[maxn];
void dfs2(int u, int f){
    int k = 0;
    pos[u] = ++sz; szu[sz] = u; belong[u]= f;
    for(int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if (dep[v] > dep[u] && siz[v] > siz[k]) k = v;
    }
    if (!k) return;
    dfs2(k, f);
    for(int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if (dep[v] > dep[u] && v != k) dfs2(v, v);
    }
}

#define Lson (k<<1)
#define Rson ((k<<1)|1)

//segment tree
struct Seg{
    int l, r, prefix, suffix, maxseq, sum, setv;
    void setVal(int val){
        int tmp = val * (r - l + 1);
        sum = tmp;
        prefix = suffix = maxseq = max(tmp, val);
        setv = val;
    }
}t[maxn<<2];

Seg operator + (Seg a, Seg b){
    if (a.maxseq == -inf) return b;
    if (b.maxseq == -inf) return a;

    Seg re;

    re.sum = a.sum + b.sum;

    re.prefix = max(a.prefix, a.sum + b.prefix);

    re.suffix = max(b.suffix, a.suffix + b.sum);

    re.maxseq = max(max(a.maxseq, b.maxseq), a.suffix + b.prefix);

    return re;
}

int w[maxn];

void pushup(int k){
    t[k].sum = t[Lson].sum + t[Rson].sum;

    t[k].prefix = max(t[Lson].prefix, t[Lson].sum + t[Rson].prefix);

    t[k].suffix = max(t[Rson].suffix, t[Lson].suffix + t[Rson].sum);

    t[k].maxseq = max(max(t[Lson].maxseq, t[Rson].maxseq), t[Lson].suffix + t[Rson].prefix);
}

void build(int k, int l, int r){
    t[k].setv = inf;
    t[k].l = l; t[k].r = r;
    if (l==r){
        t[k].prefix = t[k].suffix = t[k].maxseq = t[k].sum = w[szu[l]];
        return;
    }
    int mid = (l+r)>>1;
    build(Lson, l, mid);
    build(Rson, mid+1, r);
    pushup(k);
}

void pushdown(int k){
    if (t[k].setv != inf){
        t[Lson].setVal(t[k].setv);
        t[Rson].setVal(t[k].setv);

        t[k].setv = inf;
    }
}

void change(int k, int x, int y, int val){
    int l = t[k].l, r = t[k].r, mid = (l+r)>>1;
    if (x <= l && r <= y){
        t[k].setVal(val);
        return;
    }
    pushdown(k);
    if (y <= mid) change(Lson, x, y, val);
    else if (x > mid) change(Rson, x, y, val);
    else change(Lson, x, mid, val), change(Rson, mid+1, y, val);
    pushup(k);
}

void solveChange(int x, int y, int k){
    while(belong[x] != belong[y]){
        if (dep[belong[x]] < dep[belong[y]]) swap(x, y);
        change(1, pos[belong[x]], pos[x], k);
        x = fa[belong[x]];
    }
    if (dep[x] < dep[y]) swap(x, y);
    change(1, pos[y], pos[x], k);
}

Seg query(int k, int x, int y){
    int l = t[k].l, r = t[k].r, mid = (l+r)>>1;
    if (x <= l && r <= y){
        return t[k];
    }
    pushdown(k);
    if (y <= mid) return query(Lson, x, y);
    else if (x > mid) return query(Rson, x, y);
    else return query(Lson, x, mid) + query(Rson, mid+1, y);
}

void solveMax(int x, int y){
    Seg tx, ty;
    tx.maxseq = -inf;
    ty.maxseq = -inf;
    while(belong[x] != belong[y]){
        if (dep[belong[x]] > dep[belong[y]]){
            tx = query(1, pos[belong[x]], pos[x]) + tx;
            x = fa[belong[x]];
        }
        else{
            ty = query(1, pos[belong[y]], pos[y]) + ty;
            y = fa[belong[y]];
        }
    }
    if (dep[x] > dep[y]){
        tx = query(1, pos[y], pos[x]) + tx;
    }
    else{
        ty = query(1, pos[x], pos[y]) + ty;
    }
    swap(tx.prefix, tx.suffix);
    tx = tx + ty;
    printf("%d\n", tx.maxseq);
}

int main()
{
    int n, Q, op, x, y, z;
    scanf("%d%d", &n, &Q);
    for(int i = 1; i <= n; i++){
        scanf("%d", &w[i]);
    }
    for(int _i = 1; _i < n; _i++){
        scanf("%d%d", &x, &y);
        addedge(x, y);
        addedge(y, x);
    }
    dfs1((1+n)/2);
    dfs2((1+n)/2, (1+n)/2);
    build(1, 1, n);

    while(Q--){
        scanf("%d%d%d%d", &op, &x, &y, &z);
        if (op == 1){
            solveChange(x, y, z);
        }
        else{
            solveMax(x, y);
        }
    }
    return 0;
}
#include<bits/stdc++.h> #define int long long using namespace std; const int maxn = 1e5 + 24; struct edge{ int u,v,w; }; int n,m,q,cnt; int f[maxn],fa[maxn],siz[maxn],son[maxn],h[maxn],id[maxn],front[maxn],val[maxn << 2],a[maxn],t[maxn]; vector<edge> edges; vector<pair<int,int>> G[maxn]; bool cmp(edge a,edge b){ return a.w > b.w; } int find(int x){ return (x == f[x] ? x : f[x] = find(f[x])); } bool merge(int x,int y){ x = find(x); y = find(y); if(x == y){ return false; } f[x] = y; return true; } void dfs1(int u,int father){ fa[u] = father; h[u] = h[father] + 1; siz[u] = 1; for(auto [v,w] : G[u]){ if(v != father){ dfs1(v,u); siz[u] += siz[v]; a[v] = w; if(!son[u] || siz[son[u]] < siz[v]){ son[u] = v; } } } } void dfs2(int u,int top){ front[u] = top; id[u] = ++cnt; t[cnt] = a[u]; if(!son[u]){ return; } dfs2(son[u],top); for(auto [v,w] : G[u]){ if(v != son[u] && v != fa[u]){ dfs2(v,v); } } } void pushup(int k){ val[k] = min(val[k << 1],val[k << 1 | 1]); } void build(int k,int l,int r){ if(l == r){ val[k] = t[l]; return; } int mid = (l + r) >> 1; build(k << 1,l,mid); build(k << 1 | 1,mid + 1,r); pushup(k); } int query(int k,int l,int r,int x,int y){ if(x <= l && r <= y){ return val[k]; } int mid = (l + r) >> 1,ans = INT_MAX; if(x <= mid){ ans = query(k << 1,l,mid,x,y); } if(y > mid){ ans = min(ans,query(k << 1 | 1,mid + 1,r,x,y)); } return ans; } int ask(int x,int y){ int ans = INT_MAX; while(front[x] != front[y]){ if(h[x] < h[y]){ swap(x,y); } ans = min(ans,query(1,1,n + 1,id[front[x]] + 1,id[x])); x = fa[front[x]]; } if(h[x] > h[y]){ swap(x,y); } if(x != y){ ans = min(ans,query(1,1,n + 1,id[x] + 1,id[y])); } return ans; } void solve(){ cin >> n >> m >> q; for(int i = 1;i <= n;i++){ f[i] = i; edges.push_back({n + 1,i,-1}); } for(int i = 1;i <= m;i++){ int u,v,w; cin >> u >> v >> w; edges.push_back({u,v,w}); } sort(edges.begin(),edges.end(),cmp); for(auto i : edges){ int u = i.u,v = i.v,w = i.w; if(merge(u,v)){ G[u].push_back({v,w}); G[v].push_back({u,w}); } } dfs1(1,0); dfs2(1,1); build(1,1,n + 1); for(int i = 1;i <= q;i++){ int x,y; cin >> x >> y; cout << ask(x,y) << endl; } } signed main(){ int T; ios::sync_with_stdio(0); cin.tie(0); T = 1; while(T--){ solve(); } return 0; } 我的代码有什么问题,请你指出
08-28
内容概要:本文介绍了一个基于MATLAB实现的无人机三维路径规划项目,采用蚁群算法(ACO)与多层感知机(MLP)相结合的混合模型(ACO-MLP)。该模型通过三维环境离散化建模,利用ACO进行全局路径搜索,并引入MLP对环境特征进行自适应学习与启发因子优化,实现路径的动态调整与多目标优化。项目解决了高维空间建模、动态障碍规避、局部最优陷阱、算法实时性及多目标权衡等关键技术难题,结合并行计算与参数自适应机制,提升了路径规划的智能性、安全性工程适用性。文中提供了详细的模型架构、核心算法流程及MATLAB代码示例,涵盖空间建模、信息素更新、MLP训练与融合优化等关键步骤。; 适合人群:具备一定MATLAB编程基础,熟悉智能优化算法与神经网络的高校学生、科研人员及从事无人机路径规划相关工作的工程师;适合从事智能无人系统、自动驾驶、机器人导航等领域的研究人员; 使用场景及目标:①应用于复杂三维环境下的无人机路径规划,如城市物流、灾害救援、军事侦察等场景;②实现飞行安全、能耗优化、路径平滑与实时避障等多目标协同优化;③为智能无人系统的自主决策与环境适应能力提供算法支持; 阅读建议:此资源结合理论模型与MATLAB实践,建议读者在理解ACO与MLP基本原理的基础上,结合代码示例进行仿真调试,重点关注ACO-MLP融合机制、多目标优化函数设计及参数自适应策略的实现,以深入掌握混合智能算法在工程中的应用方法。
### 树链剖分与动态规划的结合用法 树链剖分是一种用于处理树上路径查询修改的技术,通过两次深度优先搜索 (DFS) 将树转化为线性区间,并利用数据结构(如线段树或树状数组)加速操作。动态规划则可以通过预处理子问题的答案来减少冗余计算。两者的结合主要体现在对树上的某些属性进行快速更新查询时,使用动态规划的思想优化状态转移。 以下是具体的实现方法以及代码示例: #### 实现原理 1. **树链剖分的核心** 使用两次 DFS 完成轻重边划分,将树划分为若干条重链。每条重链对应一个连续的编号范围,便于后续在线段树或其他数据结构中进行区间操作[^4]。 2. **动态规划的状态定义** 假设我们需要维护某种树节点之间的关系(例如最大值、最小值或总),可以定义 DP 状态 `dp[u]` 表示以节点 `u` 为根的子树中的某个最优解。对于不同类型的题目,DP 的具体含义会有所不同。 3. **状态转移方程** 利用树链剖分后的父子关系,可以从父节点向子节点传递信息,或者反过来从子节点向上汇总到父节点。这种自底向上的方式非常适合动态规划的应用场景。 4. **结合线段树/树状数组** 如果需要频繁地对某条路径上的数值进行修改或查询,则可以在树链剖分的基础上引入线段树等辅助工具,进一步提升效率。 --- #### 代码示例:树链剖分 + 动态规划 下面是一个典型的例子——在树上求路径的最大权值。我们将结合树链剖分动态规划完成这一目标。 ```python from collections import defaultdict class TreeChainDecomposition: def __init__(self, n): self.n = n self.graph = [[] for _ in range(n)] self.parent = [-1] * n self.depth = [0] * n self.size = [0] * n self.heavy = [-1] * n self.chain_idx = [0] * n self.pos_in_base = [0] * n self.base_array = [] def add_edge(self, u, v): self.graph[u].append(v) self.graph[v].append(u) def first_dfs(self, root=0): # 计算 size heavy 子节点 stack = [(root, -1)] while stack: node, prev_node = stack.pop() if prev_node != -1 and self.parent[node] == -1: self.parent[node] = prev_node self.depth[node] = self.depth[prev_node] + 1 sub_size = 0 max_subtree = -1 for child in self.graph[node]: if child != prev_node: stack.append((child, node)) sub_size += 1 if max_subtree == -1 or self.size[child] > self.size[max_subtree]: max_subtree = child self.size[node] = sub_size + 1 self.heavy[node] = max_subtree def second_dfs(self, root=0, chain_root=-1): # 给定重链索引并分配 base 数组位置 pos = len(self.base_array) self.chain_idx[root] = chain_root if chain_root != -1 else root self.pos_in_base[root] = pos self.base_array.append(root) if self.heavy[root] != -1: # 处理重儿子 self.second_dfs(self.heavy[root], self.chain_idx[root]) for child in self.graph[root]: # 非重儿子单独形成新链 if child != self.parent[root] and child != self.heavy[root]: self.second_dfs(child, child) def update_segment_tree(tree, idx, value, start, end, seg_pos=1): if start == end: tree[seg_pos] = value return mid = (start + end) // 2 if idx <= mid: update_segment_tree(tree, idx, value, start, mid, seg_pos*2) else: update_segment_tree(tree, idx, value, mid+1, end, seg_pos*2+1) tree[seg_pos] = max(tree[seg_pos*2], tree[seg_pos*2+1]) def query_segment_tree(tree, l, r, start, end, seg_pos=1): if l > end or r < start: return float('-inf') if l <= start and end <= r: return tree[seg_pos] mid = (start + end) // 2 left_query = query_segment_tree(tree, l, r, start, mid, seg_pos*2) right_query = query_segment_tree(tree, l, r, mid+1, end, seg_pos*2+1) return max(left_query, right_query) # 初始化树输入样例 n = 8 edges = [[0, 1], [0, 2], [1, 3], [1, 4], [2, 5], [2, 6], [6, 7]] weights = {i: i+1 for i in range(n)} # 节点权重 tcd = TreeChainDecomposition(n) for u, v in edges: tcd.add_edge(u, v) tcd.first_dfs() # 第一次 DFS 构建大小重儿子信息 tcd.second_dfs() # 第二次 DFS 进行链划分 segment_tree = [float('-inf')] * (len(tcd.base_array)*4) # 创建线段树 for i in range(len(tcd.base_array)): update_segment_tree(segment_tree, i, weights[tcd.base_array[i]], 0, len(tcd.base_array)-1) # 查询路径最大值函数 def query_path_max(node_u, node_v): result = float('-inf') while tcd.chain_idx[node_u] != tcd.chain_idx[node_v]: if tcd.depth[tcd.chain_idx[node_u]] < tcd.depth[tcd.chain_idx[node_v]]: node_u, node_v = node_v, node_u current_chain_head = tcd.chain_idx[node_u] pos_start = tcd.pos_in_base[current_chain_head] pos_end = tcd.pos_in_base[node_u] result = max(result, query_segment_tree(segment_tree, pos_start, pos_end, 0, len(tcd.base_array)-1)) node_u = tcd.parent[current_chain_head] last_chain_head = tcd.chain_idx[node_u] pos_start = min(tcd.pos_in_base[node_u], tcd.pos_in_base[node_v]) pos_end = max(tcd.pos_in_base[node_u], tcd.pos_in_base[node_v]) result = max(result, query_segment_tree(segment_tree, pos_start, pos_end, 0, len(tcd.base_array)-1)) return result # 测试查询 print(query_path_max(0, 7)) # 输出路径最大值 ``` 上述代码展示了如何结合树链剖分动态规划解决问题的过程。其中,动态规划的部分体现在对每个节点的最终权值进行了预先存储,并通过线段树支持高效的区间查询[^5]。 --- ### 总结 树链剖分提供了强大的工具来简化树形结构的操作,而动态规划能够有效降低复杂度。两者结合后,在许多实际应用中表现出色,尤其是在涉及大量路径查询的情况下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值