线段树进阶总结一 DFS序 欧拉序(括号序)

DFS序

前置的几道题

线段树DFS序 1 单点更新 区间查询
https://blog.csdn.net/qq_40831340/article/details/84501232
线段树DFS序 2 区间子树更新 单点查
https://blog.csdn.net/qq_40831340/article/details/84501478

以上都是关于统计 跟改子树的

欧拉序

这里我们可以处理出 关于树上路径的
虽然我觉得 都是DFS搞这数组也没有啥大区别

树上操作

https://www.luogu.org/problem/P3178
其实我代码没有过样例 能过题的原因 是我读错题了 样例是父亲指向儿子 可是数据全是 儿子指向父亲 23333
我可能是出题人以外知道这个事情的人了
这次我们要处理 树上路径问题 要是像之前2篇博客内样 肯定不行 我们选择要剪掉 out 的才好
不能再把他们压在一个结构体了 当然 我之前那样写 也是没有想过会有这样的问题

#include 
#define int long long
using namespace std;
const int maxn = 2e5 + 10;

int n, m, val[maxn];
int head[maxn], cnt;
int to[maxn], nxt[maxn];

void ade(int a, int b) {
	to[++ cnt] = b;
	nxt[cnt] = head[a], head[a] = cnt;
}

int seq[maxn << 1], inout[maxn << 1];
int ind[maxn << 1], outs[maxn << 1], tot;

int ss;
void dfs(int u) {
	seq[++ tot] = u, inout[tot] = 1, ind[u] = tot;
	for(int i = head[u]; i; i = nxt[i]) 
		dfs(to[i]),ss ++;
	seq[++ tot] = u, inout[tot] = -1, outs[u] = tot;
}

struct node {
	int sum, lazy, flag;
} tree[maxn << 3];

void push_up(int rt) {
	tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
	tree[rt].flag = tree[rt << 1].flag + tree[rt << 1 | 1].flag;
}

void push_down(int rt) {
	tree[rt << 1].sum += tree[rt << 1].flag * tree[rt].lazy;
	tree[rt << 1].lazy += tree[rt].lazy;
	tree[rt << 1 | 1].sum += tree[rt << 1 | 1].flag * tree[rt].lazy;
	tree[rt << 1 | 1].lazy += tree[rt].lazy;
	tree[rt].lazy = 0;
}

void build(int l, int r, int rt) {
	if(l == r) {
		tree[rt].flag = inout[l];
		tree[rt].sum = inout[l] * val[seq[l]];
		return ;
	}
	int mid = l + r >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	push_up(rt);
}

void updata(int L, int R, int l, int r, int rt, int val) {
	if(L <= l && R >= r) {
		tree[rt].sum += tree[rt].flag * val;
		tree[rt].lazy += val;
		return ;
	}
	if(tree[rt].lazy) push_down(rt);
	int mid = l + r >> 1;
	if(L <= mid) updata(L, R, l, mid, rt << 1, val);
	if(R > mid) updata(L, R, mid + 1, r, rt << 1 | 1, val);
	push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
	if(L <= l && R >= r) {
		return tree[rt].sum;
	}
	if(tree[rt].lazy) push_down(rt);
	int mid = r + l >> 1;
	int res = 0;
	if(L <= mid) res += query(L, R, l, mid, rt << 1);
	if(R > mid) res += query(L, R, mid + 1, r, rt << 1 | 1);
	return res;
}

signed main() {
	scanf("%lld %lld", &n, &m);
	for(int i = 1; i <= n; i ++ ) scanf("%lld", &val[i]);
	for(int i = 1, a, b; i < n; i ++){
			scanf("%lld %lld", &a, &b), ade(b, a);
	}
	
	int N = n << 1;
	dfs(1);
	build(1, N, 1);
	int op, x, a;

	while(m --) {
		scanf("%lld %lld", &op, &x);
		if(op == 1) {
			scanf("%lld", &a);
			updata(ind[x], ind[x], 1, N, 1, a);
			updata(outs[x], outs[x], 1, N, 1, a);
		} else if(op == 2) {
			scanf("%lld", &a);
			updata(ind[x], outs[x], 1, N, 1, a);
		} else {
			printf("%lld\n", query(1, ind[x], 1, N, 1));
		}
	}
	return 0;
}

树上路径 区间查 单点改

接下来就是我们 dfs建立括号序列 如果让路上某个点+v
路径询问 我们增加了多少
就可以考虑 根节点 到 u 根节点到 v 我们再减去 2倍根节点 到 lca 再加上 lca

#include 
//#define int long long
using namespace std;
const int maxn = 2e5 + 10;

int n, m, val[maxn];
int head[maxn], cnt;
int to[maxn], nxt[maxn];

void ade(int a, int b) {
	to[++ cnt] = b;
	nxt[cnt] = head[a], head[a] = cnt;
}

int seq[maxn << 1], inout[maxn << 1];
int ind[maxn << 1], outs[maxn << 1], tot;

void dfs(int u, int pre) {
	seq[++ tot] = u, inout[tot] = 1, ind[u] = tot;
	for(int i = head[u]; i; i = nxt[i])
		if(pre != to[i]) dfs(to[i], u);
	seq[++ tot] = u, inout[tot] = -1, outs[u] = tot;
}

struct node {
	int sum, flag;
} tree[maxn << 2];

void push_up(int rt) {
	tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}

void build(int l, int r, int rt) {
	if(l == r) {
		tree[rt].flag = inout[l];
		tree[rt].sum = inout[l] * val[seq[l]];
		return ;
	}
	int mid = l + r >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	push_up(rt);
}

void updata(int L, int l, int r, int rt, int val) {
	if(l == r) {
		tree[rt].sum = tree[rt].flag * val;
		return ;
	}
	int mid = l + r >> 1;
	if(L <= mid) updata(L, l, mid, rt << 1, val);
	else updata(L, mid + 1, r, rt << 1 | 1, val);
	push_up(rt);
}

int query(int L, int R, int l, int r, int rt) {
	if(L == 0) return 0;
	if(L <= l && R >= r) {
		return tree[rt].sum;
	}
	int mid = r + l >> 1;
	int res = 0;
	if(L <= mid) res += query(L, R, l, mid, rt << 1);
	if(R > mid) res += query(L, R, mid + 1, r, rt << 1 | 1);
	return res;
}

int fa[maxn][30], depth[maxn];
int lg[maxn];

void lca_dfs(int x,int pre) {
	depth[x] = depth[pre] + 1;
	fa[x][0] = pre;
	for(int i = 1; (1 << i) <= depth[x]; i ++)
		fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int i = head[x]; i; i = nxt[i])
		if(to[i] != pre) {
			lca_dfs(to[i], x);
		}
}

int LCA(int x,int y) {
	if(depth[x] < depth[y]) swap(x, y);
	while(depth[x] > depth[y]) x = fa[x][lg[depth[x] - depth[y]] - 1];
	if(x == y) return x;
	for(int k = lg[depth[x]] - 1; k >= 0; k --) {
		if(fa[x][k] != fa[y][k]) {
			x = fa[x][k], y = fa[y][k];
		}
	}
	return fa[x][0];
}

signed main() {
	freopen("in.in", "r", stdin);
	freopen("a.out","w+",stdout);
	for(int i = 1; i < maxn; i ++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i ++ ) scanf("%d", &val[i]);
	for(int i = 1, a, b; i < n; i ++)
		scanf("%d %d", &a, &b), ade(a, b), ade(b, a);

	int N = n << 1;
	dfs(1, 0);
	build(1, N, 1);
	lca_dfs(1, 0);
	fa[1][0] = 0;
	int op , u, v;
	while(m --) {
		scanf("%d %d %d", &op, &u, &v);
		if(op == 1) {
			updata(ind[u], 1, N, 1, v);
			updata(outs[u], 1, N, 1, v);
		} else {
			int lca = LCA(u, v);
			printf("%d\n", \
			       query(1, ind[u], 1, N, 1) + \
			       query(1, ind[v], 1, N, 1) - \
			       2 * query(1, ind[lca], 1, N, 1) + \
			       query(ind[lca], ind[lca], 1, N, 1) \
			      );
		}
	}
	return 0;
}

区间改 就特别需要debug能力拉 orz 之后补上 还是不会orz

你可能感兴趣的:(线段树)