CodeForces 838B - Diverging Directions (线段树,dfs序,LCA)

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;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81570236