// // // // // // // // // // // // // //

线段树 篇五(动态开点)

线段树的动态开点

前言:

以前写的 搬到了这里 那一篇会删掉的

对于线段树一 又写了一遍 代码会放到最后

引入:

众所周知 线段树是一棵完全二叉树 父节点与子节点具有二倍编号的原则 所以一般的线段树长得板板正正 而且它很友好的占用的四倍的空间

而当给出的数据比较大 需要建上数坨的线段树的时候 就会出现空间不足的情况 这个时候就可以动态开点 随开随用 不将整个线段树一下子建好 而是先建一个根节点 其他节点在用的时候再补充(Q:你把主席树放在什么位置了? A:滚!)

主席树也是动态开点的吧 没有学过 所以我就不扯了

采用动态开点的方式可以有效的降低时间复杂度 但是同时我们破坏了二叉树的二倍标号规则 所以改为使用变量记录左右子节点的编号 同时 它也不再保存每个节点代表的区间 而是在每次递归访问的过程中作为参数传递(我写惯了结构体形式的 所以感觉不是很习惯0.0)

例题:洛谷 P3313 旅行

题目:

思路:

本来这是一个很友好的树剖题目 但是由于各种 邪教 宗教的出现 这就变成了一个毒瘤题

首先是线段树的结构:

const int D = 1e7 + 7;
struct node {
    int l, r, maxx, sum;
}t[D << 1];

这里的 l 和 r 不再是区间的边界 而是表示这一节点的左右孩子

而对于每一个宗教 我们都要开一个线段树
不再有建树操作 而是直接在更新时添加新的树根

void up_date(int &p, int l, int r, int k, int ps)
{
    if(!p) p = ++tcnt;
    t[p].maxx = max(t[p].maxx, k); t[p].sum += k;
    if(l == r) return ; int mid = (l + r) >> 1;
    if(mid >= ps) up_date(ls(p), l, mid, k, ps);
    else up_date(rs(p), mid + 1, r, k, ps);
}

对于宗教的更改 直接将这一点的宗教改掉 把这一节点中的各种值直接付为零 对于它到根节点的路径上的所有节点通过 push_up 更新
在根改变后的线段树中将点权更新

void remove(int &p, int l, int r, int ps)
{
    if(l == r) {t[p].sum = t[p].maxx = 0; return ;}
    int mid = (l + r) >> 1;
    if(mid >= ps) remove(ls(p), l, mid, ps);
    else remove(rs(p), mid + 1, r, ps);
    t[p].sum = t[ls(p)].sum + t[rs(p)].sum;
    t[p].maxx = max(t[ls(p)].maxx, t[rs(p)].maxx);
}

而对于查询操作 更改不大 只是由原来不断收缩区间以确定范围变成了直接查找左右孩子 毕竟不再是完全二叉树 不会再有一堆一堆的节点没有用

int query_max(int p, int l, int r, int L, int R)
{
    if(r < L || R < l) return 0;
    if(L <= l && r <= R) return t[p].maxx;
    int mid = (l + r) >> 1;
    return max(query_max(ls(p), l, mid, L, R), query_max(rs(p), mid + 1, r, L, R));
}
int query_sum(int p, int l, int r, int L, int R)
{
    if(r < L || R < l) return 0;
    if(L <= l && r <= R) return t[p].sum;
    int mid = (l + r) >> 1;
    return query_sum(ls(p), l, mid, L, R) + query_sum(rs(p), mid + 1, r, L, R);
}

然后套上树剖的板子就 OK 了

code:

/*
  Time: 2.3
  Worker: Blank_space
  Source: #2195. 「SDOI2014」旅行
  
  好诡异的动态开点 感觉这个线段树长歪了的说 
*/
/*--------------------------------------------*/
#include<cstdio>
#include<vector>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int FFF = 0x8fffffff;
/*------------------------------------常量定义*/
int n, q, _p[B], c[B], w[B];
char s[10];
vector <int> e[B];
int dfn[B], pos[B], dep[B], top[B], siz[B], son[B], fa[B], cnt, tcnt;
/*------------------------------------变量定义*/
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
    #define ls(x) t[x].l
    #define rs(x) t[x].r
    struct node {
        int l, r, maxx, sum;
    }t[D << 1];
    void up_date(int &p, int l, int r, int k, int ps)
    {
        if(!p) p = ++tcnt;
	t[p].maxx = max(t[p].maxx, k); t[p].sum += k;
	if(l == r) return ; int mid = (l + r) >> 1;
	if(mid >= ps) up_date(ls(p), l, mid, k, ps);
	else up_date(rs(p), mid + 1, r, k, ps);
    }
    void remove(int &p, int l, int r, int ps)
    {
	if(l == r) {t[p].sum = t[p].maxx = 0; return ;}
	int mid = (l + r) >> 1;
	if(mid >= ps) remove(ls(p), l, mid, ps);
	else remove(rs(p), mid + 1, r, ps);
	t[p].sum = t[ls(p)].sum + t[rs(p)].sum;
	t[p].maxx = max(t[ls(p)].maxx, t[rs(p)].maxx);
    }
    int query_max(int p, int l, int r, int L, int R)
    {
	if(r < L || R < l) return 0;
	if(L <= l && r <= R) return t[p].maxx;
	int mid = (l + r) >> 1;
	return max(query_max(ls(p), l, mid, L, R), query_max(rs(p), mid + 1, r, L, R));
    }
    int query_sum(int p, int l, int r, int L, int R)
    {
	if(r < L || R < l) return 0;
	if(L <= l && r <= R) return t[p].sum;
	int mid = (l + r) >> 1;
	return query_sum(ls(p), l, mid, L, R) + query_sum(rs(p), mid + 1, r, L, R);
    }
}
namespace Cut {
    void dfs(int u, int pre)
    {
	dep[u] = dep[pre] + 1; fa[u] = pre; siz[u] = 1;
	for(int i = 0; i < e[u].size(); i++)
	{
	    int v = e[u][i];
      	    if(v == pre) continue;
	    dfs(v, u); siz[u] += siz[v];
	    if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
	}
    }
    void dfs2(int u, int tp)
    {
	top[u] = tp; dfn[u] = ++cnt; pos[cnt] = u;
	if(!son[u]) return ; dfs2(son[u], tp);
	for(int i = 0; i < e[u].size(); i++)
	{
	    int v = e[u][i];
	    if(v == fa[u] || v == son[u]) continue;
	    dfs2(v, v);
	}
    }
    int query_max(int x, int y, int z)
    {
	int res = 0;
	while(top[x] != top[y])
	{
	    if(dep[top[x]] < dep[top[y]]) swap(x, y);
	    res = max(res, Seg::query_max(_p[z], 1, n, dfn[top[x]], dfn[x]));
	    x = fa[top[x]];
	}
	if(dfn[x] > dfn[y]) swap(x, y);
	res = max(res, Seg::query_max(_p[z], 1, n, dfn[x], dfn[y]));
	return res;
    }
    int query_sum(int x, int y, int z)
    {
	int res = 0;
	while(top[x] != top[y])
	{
	    if(dep[top[x]] < dep[top[y]]) swap(x, y);
	    res += Seg::query_sum(_p[z], 1, n, dfn[top[x]], dfn[x]);
	    x = fa[top[x]];
	}
	if(dfn[x] > dfn[y]) swap(x, y);
	res += Seg::query_sum(_p[z], 1, n, dfn[x], dfn[y]);
	return res;
    }
}
/*----------------------------------------函数*/
int main()
{
    n = read(); q = read();
    for(int i = 1; i <= n; i++) w[i] = read(), c[i] = read();
    for(int i = 1; i < n; i++)
    {
	int x = read(), y = read();
	e[x].push_back(y); e[y].push_back(x);
    }
    Cut::dfs(1, 0); Cut::dfs2(1, 1);
    for(int i = 1; i <= n; i++) Seg::up_date(_p[c[i]], 1, n, w[i], dfn[i]);
    for(int i = 1; i <= q; i++)
    {
	scanf("%s", s); int x = read(), y = read();
	if(s[1] == 'C') {Seg::remove(_p[c[x]], 1, n, dfn[x]); Seg::up_date(_p[y], 1, n, w[x], dfn[x]); c[x] = y;}
	if(s[1] == 'W') {Seg::remove(_p[c[x]], 1, n, dfn[x]); Seg::up_date(_p[c[x]], 1, n, y, dfn[x]); w[x] = y;}
	if(s[1] == 'S') printf("%d\n", Cut::query_sum(x, y, c[x]));
	if(s[1] == 'M') printf("%d\n", Cut::query_max(x, y, c[x]));
    }
    return 0;
}

线段树 \(1\) 的代码

/*
  Time: 3.19
  Worker: Blank_space
  Source: P3372 【模板】线段树 1
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Abs(x) ((x) < 0 ? -(x) : (x))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, cnt, p;
/*------------------------------------变量定义*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
	#define ls(x) t[x].l
	#define rs(x) t[x].r
	struct node {int l, r, sum, lzy;}t[B << 1];
	void up_date(int &p, int l, int r, int L, int R, int k) {
		if(!p) p = ++cnt; int len = Min(r, R) - Max(l, L) + 1; t[p].sum += len * k;
		if(L <= l && r <= R) {t[p].lzy += k; return ;}
		int mid = l + r >> 1;
		if(L <= mid) up_date(ls(p), l, mid, L, R, k);
		if(R > mid) up_date(rs(p), mid + 1, r, L, R, k);
	}
	int query(int p, int l, int r, int L, int R) {
		if(L <= l && r <= R) return t[p].sum;
		int mid = l + r >> 1, res = 0;
		if(t[p].lzy)
		{
			if(!ls(p)) ls(p) = ++cnt; t[ls(p)].lzy += t[p].lzy; t[ls(p)].sum += t[p].lzy * (mid - l + 1);
			if(!rs(p)) rs(p) = ++cnt; t[rs(p)].lzy += t[p].lzy; t[rs(p)].sum += t[p].lzy * (r - mid);
			t[p].lzy = 0;
		}
		if(L <= mid) res += query(ls(p), l, mid, L, R);
		if(R > mid) res += query(rs(p), mid + 1, r, L, R);
		return res;
	}
}
/*----------------------------------------函数*/
signed main() {
	n = read(); m = read();
	for(int i = 1; i <= n; i++) Seg::up_date(p, 1, n, i, i, read());
	for(int i = 1; i <= m; i++)
	{
		int opt = read(), x = read(), y = read();
		if(opt == 1) {int z = read(); Seg::up_date(p, 1, n, x, y, z);}
		if(opt == 2) printf("%lld\n", Seg::query(p, 1, n, x, y));
	}
	return 0;
}


总结:

通过动态开点的方式 大大降低了空间复杂度 虽然树长歪了 但是还是非常实用的


后续如果在做题目的话会在进行更新 写到这里

—— \(end\)

posted @ 2021-03-21 13:39  Blank_space  阅读(104)  评论(0)    收藏  举报
// // // // // // //