题目链接
题意
给你一个 n n n 个节点的树,树有边权,初始位置在 s s s ,有两个操作
- 求当前位置到目标位置最短路长度,并更新当前位置
- 更改某条边边权
思路
树链剖分下,帮每条边权放在其两个连接点深度较大的节点上。然后线段树单点修改,区间查询,每次查询减去公共祖先的权值即可。
vector 建图超时(可能我代码丑
代码
#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
const int N = 1e5+5;
struct Node {
int u, v, w;
}node[N];
//vector<int> e[N];
int tot, first[N];
struct Edge {
int v, nxt;
}e[N<<1];
void add(int u, int v) {
e[tot].v = v;
e[tot].nxt = first[u];
first[u] = tot++;
}
int dep[N], f[N], siz[N], son[N];
int cnt, id[N], top[N];
void dfs1(int u, int fa) {
dep[u] = dep[fa]+1;
f[u] = fa;
siz[u] = 1;
son[u] = 0;
int maxson = -1;
// for(int i = 0; i < e[u].size(); ++i) {
// int v = e[u][i];
for(int i = first[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dfs1(v,u);
siz[u] += siz[v];
if(siz[v] > maxson) son[u] = v, maxson = siz[v];
}
}
void dfs2(int u, int topfa) {
id[u] = ++cnt;
top[u] = topfa;
if(!son[u]) return;
dfs2(son[u], topfa);
// for(int i = 0; i < e[u].size(); ++i) {
// int v = e[u][i];
for(int i = first[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if(v == son[u] || v == f[u]) continue;
dfs2(v,v);
}
}
int n, q, s;
int tr[N << 2];
void updata(int rt, int l, int r, int pos, int val) {
if(l == r) {
tr[rt] = val;
return;
}
int mid = l+r >> 1;
if(pos <= mid) updata(rt<<1,l,mid,pos,val);
else updata(rt<<1|1,mid+1,r,pos,val);
tr[rt] = tr[rt<<1]+tr[rt<<1|1];
}
int query(int rt, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) return tr[rt];
int mid = l+r >> 1;
int res = 0;
if(ql <= mid) res += query(rt<<1,l,mid,ql,qr);
if(qr > mid) res += query(rt<<1|1,mid+1,r,ql,qr);
return res;
}
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 += query(1,1,n,id[top[x]], id[x]);
x = f[top[x]];
}
if(dep[x] > dep[y]) swap(x,y);
ans += query(1,1,n,id[x],id[y]);
// printf("`` %d\n",ans);
ans -= query(1,1,n,id[x],id[x]);
return ans;
}
int main() {
scanf("%d%d%d",&n,&q,&s);
tot = 0;
memset(first,-1,sizeof(first));
for(int i = 1; i < n; ++i) {
scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w);
add(node[i].u, node[i].v);
add(node[i].v, node[i].u);
// e[node[i].u].push_back(node[i].v);
// e[node[i].v].push_back(node[i].u);
}
dfs1(1,0);
dfs2(1,1);
for(int i = 1; i < n; ++i) {
if(dep[node[i].u] > dep[node[i].v]) updata(1,1,n,id[node[i].u],node[i].w);
else updata(1,1,n,id[node[i].v],node[i].w);
}
while(q--) {
int opt, a, b;
scanf("%d",&opt);
if(opt) {
scanf("%d%d",&a,&b);
node[a].w = b;
if(dep[node[a].u] > dep[node[a].v]) updata(1,1,n,id[node[a].u],node[a].w);
else updata(1,1,n,id[node[a].v],node[a].w);
}
else {
scanf("%d",&a);
printf("%d\n",qRange(a,s));
s = a;
}
}
return 0;
}

本文介绍了一种解决树形结构中路径查询问题的高效算法:树链剖分结合线段树。通过将树分解为多个链并利用线段树进行区间查询和单点更新,该方法能快速响应边权更改和路径最短路长度查询。
392

被折叠的 条评论
为什么被折叠?



