n 个点 2n-2 条有向带权边的图,其中
- 编号 1~n-1 的边一定形成一个 1 号点为根的生成树。
- 编号n~2n-2 的边一定是从点 i ([Math Processing Error]i≠1 且 i 点各不同) 到点 1 .
有 q 个询问,询问有两种类型:
1 i w
:将第 i 号边的权值(距离)修改为 w 。2 u v
:求 u 到 v 的最短路。
思路:
我们先跑一下dfs序,给每个点弄一个时间戳,题目是要改第 i 条路径,怎么办呢?,我们把这条路径上的值下降到路径的下面节点就行了。 对于 1 这个节点,他并没有什么用,什么值都没有。
我们建一颗树,跑完dfs序之后,每个节点都会包括他的子孩子。我们改一个路径时,他的子孩子都要修改,相当于区间修改。
我们线段树每个节点维护的是 1号点到当前节点的距离,和当前节点到1 号点的距离,相当于一个圈。
对于第一个操作,
如果 路径小于 n 就是个区间修改,,如果 路径 大于等于n 就是 修改单点。
对于第二个操作。
先求 u v 的最小公共祖先, 如果 祖先就是 u 那就直接从 u 走到 v就可以了,就是查询 单点 然后 减去 当前点到1 的距离。
Query(1,l[x],l[x]) - c[x] 这个就是 1 号点到 x 的距离,
c[x] 是 x 点到 1 号点的距离。
如果 u 不是 v的祖先, 那就 从 u 。。。。。1 。。。。v
因为从1 到 u 的距离一定。然后从u 以及u的孩子都可以走到 1 。所以我们找一个最小的值就行,然后减去从1 到u的距离,
查找 u以及u的孩子到 1 的距离,就是线段树查询区间。我们维护了最小值。
然后我们在加上 1 到 v 的距离。
这个题 min函数写成了max函数,神tm,一直wa
像这种dfs序之后又用到了线段树,,一般维护的都是最小最大值,然后就是 当前节点包含的左右时间戳,
区间维护的就是当前节点以及他的孩子到 根节点的最短距离或最长距离。
如果想找当前节点到根节点的距离,我们只要找到叶子节点就行。
#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x))
#define rep(i,a,b) for (int i = a; i < b; i++)
#define per(i,a,b) for (int i = a; i > b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const LL INF = 1e18;
const int N =4e5+1000;
struct node{
int v,c,w;
};
struct segtree{
int l,r,a,b;
LL w,c;
}f[N*2];
int n,m,tim,t;
vector<node>g[N];
int dep[N],p[N][20],l[N],r[N],pre[N],d[N];
LL val[N],dis[N],a[N],c[N];
void push_down(int p){
if (f[p].c == 0) return;
f[f[p].l].w += f[p].c;
f[f[p].r].w += f[p].c;
f[f[p].l].c += f[p].c;
f[f[p].r].c += f[p].c;
f[p].c = 0;
return;
}
void build(int p, int a, int b){
f[p].a = a; f[p].b = b; f[p].c = 0;
if (a + 1 == b){
f[p].w = dis[d[a]] + c[d[a]];
return;
}
int m = (a + b) / 2;
t++; f[p].l = t; build(t,a,m);
t++; f[p].r = t; build(t,m,b);
f[p].w = min(f[f[p].l].w , f[f[p].r].w);
return;
}
void Insert(int p, int x, int y, int z){
if (x <= f[p].a && y >= f[p].b - 1){
f[p].w += z;
f[p].c += z;
return;
}
push_down(p);
int m = (f[p].a + f[p].b) / 2;
if (x < m) Insert(f[p].l,x,y,z);
if (y >= m) Insert(f[p].r,x,y,z);
f[p].w = min(f[f[p].l].w, f[f[p].r].w);
return;
}
LL Query(int p, int x, int y){
LL ans = INF;
if (x <= f[p].a && y >= f[p].b -1){
return f[p].w;
}
push_down(p);
int m = (f[p].a + f[p].b) / 2;
if (x < m) ans = min(ans, Query(f[p].l,x,y));
if (y >= m) ans = min(ans,Query(f[p].r,x,y));
return ans;
}
void dfs(int u, int value, int deep, int fa){
l[u] = ++tim; dis[u] = value; dep[u] = deep;
p[u][0] = fa; d[tim] = u;
for (int i = 1; i < 20; i++) p[u][i] = p[p[u][i-1]][i-1];
for (int i = 0; i < g[u].size(); i++) {
pre[g[u][i].c] = g[u][i].v; val[g[u][i].v] = g[u][i].w;
dfs(g[u][i].v, value + g[u][i].w, deep + 1,u);
}
r[u] = tim;
return;
}
int lca(int x, int y){
if (dep[x] < dep[y])swap(x,y);
per(i,19,-1)
if (dep[p[x][i]] >= dep[y]) x = p[x][i];
if (x == y) return x;
per(i,19,-1)
if (p[x][i] != p[y][i]){
x = p[x][i];
y = p[y][i];
}
return p[x][0];
}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n){
int x,y;LL z;
scanf("%d%d%I64d",&x,&y,&z);
node e;
e.v = y; e.c = i; e.w = z; g[x].push_back(e);
}
rep(i,1,n){
int x,y; LL z;
scanf("%d%d%I64d",&x,&y,&z);
c[x] = z;
a[i] = x;
}
tim = 0; c[1] = 0;
dfs(1,0,1,0);
t = 1;
build(1,1,n+1);
while(m--){
int op,x; LL y;
scanf("%d",&op);
if (op == 1){
scanf("%d%I64d",&x,&y);
if (x < n){
x = pre[x];
Insert(1,l[x],r[x],y-val[x]);
val[x] = y;
} else{
x = a[x-n+1];
Insert(1,l[x],l[x],y-c[x]);
c[x] = y;
}
} else{
scanf("%d%I64d",&x,&y);
int k = lca(x,y);
LL ans;
if (k == x){
ans = Query(1,l[y],l[y]) - Query(1,l[x],l[x]) + c[x] - c[y];
} else{
ans = Query(1,l[x],r[x]);
LL dx = Query(1,l[x],l[x]) - c[x];
LL dy = Query(1,l[y],l[y]) - c[y];
ans = ans - dx + dy;
}
printf("%I64d\n",ans);
}
}
return 0;
}