线段树 篇五(动态开点)
线段树的动态开点
前言:
以前写的 搬到了这里 那一篇会删掉的
对于线段树一 又写了一遍 代码会放到最后
引入:
众所周知 线段树是一棵完全二叉树 父节点与子节点具有二倍编号的原则 所以一般的线段树长得板板正正 而且它很友好的占用的四倍的空间
而当给出的数据比较大 需要建上数坨的线段树的时候 就会出现空间不足的情况 这个时候就可以动态开点 随开随用 不将整个线段树一下子建好 而是先建一个根节点 其他节点在用的时候再补充(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\)

浙公网安备 33010602011771号