首页 > 其他分享 >数据 tree or binary

数据 tree or binary

时间:2024-05-23 16:11:15浏览次数:18  
标签:binary int tr ll tree tag 区间 数据 op

ST 表

本来不想写的,但是我考试因为 ST表写错,痛失 \(100\) 分,想想还是写吧

简介

原型是倍增,不过它是用来求区间最值(其实也可以求和),而且是静态的(不如线段树),区间最值也可以写成:\(RMQ\) 问题,ST表可以让查询最值达到 \(O(logn)\),算是很高效了。

思路

将区间dp的 \(dp[i][j]\) 变成 \(f[i][j - i + 1]\) 然后
\(j - i + 1\),在特定的情况下可以表示为 \(2^k\),而 \(k\) 的范围是 \(log2(n)\) 所以可以枚举 \(k\)。然后两个小区间可以接起来成为一个大区间,这是区间dp的思想,利用这一点我们可以得到:\(f[i][j] = max/min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1])\) 预处理就写完了。但是由于我们预处理的其实是一个特定的情况,也就是 \(2^k\),所以我们可以将你要求的区间长度转换成二进制,然后逐个处理,也就是说对于长度的二进制的第 \(i\) 位是 \(1\),那么答案就涵盖 \(f[l][i]\),然后逐个求解。

代码

结构体

#include <cmath>
#include <iostream>

using namespace std;

const int MaxN = 5e4 + 10, MaxK = 17;

struct ST {
  int f[MaxN][MaxK];  // 倍增数组
  bool op;            // 是求大值(1),还是小值(0)

  int cmp(int x, int y) {  // 比较函数
    return op ? max(x, y) : min(x, y);
  }

  void init(int a[], int n, bool how) {  // 初始化
    op = how;
    for (int i = 1; i <= n; i++) {
      f[i][0] = a[i];
    }
    for (int j = 1; j < MaxK; j++) {
      for (int i = 1; i + (1 << j) - 1 <= n; i++) {
        f[i][j] = cmp(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
      }
    }
  }

  int find(int l, int g) {  // 求答案
    int ans = op ? -1e9 : 1e9;
    for (int i = 0; i < MaxK; i++) {
      if (g >> i & 1) {
        ans = cmp(ans, f[l][i]);
        l += (1 << i);
      }
    }
    return ans;
  }
} l;

int main() {
  return 0;
}

树状数组

一个二进制且树的东东

单点

区间修改,是一个区间,所以我们选择差分,可是我们要都次频繁的查询,所以我们考虑类似线段树的方法通过统计已经统计过的区间来算,于是我们将一个数组依照二进制的第一位以及它的原始编号分好类,即类似于

的东西,然后插入时就一点一点往上更新,那查找的时候由于是一个一个区间往下查的,必然会成一个前缀,所以我们运用前缀和将前面的剪掉即可。

code

#include <iostream>

using namespace std;

const int MaxN = 5e5 + 10;

long long d[MaxN], n, q;

int lb(int x) {
  return x & (-x);
}

void in(int id, int x) {
  for (int i = id; i <= n; d[i] += x, i += lb(i)) {
  }
}

long long Sum(int x) {
  long long res = 0;
  for (int i = x; i; res += d[i], i -= lb(i)) {
  }
  return res;
}

int main() {
  cin >> n >> q;
  for (int i = 1, x; i <= n; i++) {
    cin >> x, in(i, x);
  }
  for (int op, l, r; q; q--) {
    cin >> op >> l >> r;
    if (op == 1) {
      in(l, r);
    } else {
      cout << Sum(r) - Sum(l - 1) << '\n';
    }
  }
  return 0;
}

区间

推个柿子,我们如果要求区间查询:

插入还是一样的差分

查询将会变成(如果是区间 \([l,r]\) 的话,其实最终会用前缀和的方法变成一样):

\(d_1+(d_1+d_2)+(d_1+d_2+d_3)+\cdots\)

\(d_1+d_1+\cdots+d_2+d_2+\cdots\)

\(nd_1+(n-1)d_2+\cdots\)

\(n(d_1+\cdots)-0d_1-1d_2-\cdots\)

此时就变成了一个熟悉的前缀和,所以在维护一个树状数组,而对于 \(i\) 的数加入按照上式要乘一个 \(i\) 那对于区间 \([l, r]\),就得用个前缀和(好像没必要推吧,常识问题?),然后区间 \([l,r]\) 的问题就解决了(还没懂我也没办法)。

code

#include <iostream>

using namespace std;
using ll = long long;

const int MaxN = 1e5 + 10;

ll g[MaxN], d[MaxN], n, q;

int lb(int x) {
  return x & (-x);
}

void G(int l, int r, ll x) {
  ll lx = (n - (n - (l - 1))) * x, rx = (n - (n - r)) * x;
  for (l; l <= n; l += lb(l)) {
    d[l] += x, g[l] += lx;
  }
  for (r++; r <= n; r += lb(r)) {
    d[r] -= x, g[r] -= rx;
  }
}

ll A(int x) {
  ll res = 0;
  for (int i = x; i; i -= lb(i)) {
    res += d[i];
  }
  res *= x;
  for (int i = x; i; i -= lb(i)) {
    res -= g[i];
  }
  return res;
} 

int main() {
  cin >> n >> q;
  for (int i = 1, x; i <= n; i++) {
    cin >> x;
    G(i, i, x);
  }
  for (int op, x, y, k; q; q--) {
    cin >> op >> x >> y;
    if (op == 1) {
      cin >> k;
      G(x, y, k);
    } else {
      cout << A(y) - A(x - 1) << endl;
    }
  }
  return 0;
}

线段树

思想

分治思想,将一个区间分成两个小区间,然后来维护一些信息

统计

默认区间。根据我们的建树,我们可以考虑将一个大区间分成几个小区间来算,同样也是分治。

修改

直接区间。考虑用一个懒标记,用来存储更改的值,然后访问这个节点时下传即可。

code

平衡树版:

#include <ctime>
#include <iostream>
#define int long long

using namespace std;

const int MaxN = 2e5 + 10;

struct Tree {
  struct Node {
    int w, v, l, r, fa, sum, tag, x;
  } a[MaxN];

  int root, tot;

  void update(int k) {
    a[k].sum = a[a[k].l].sum + a[a[k].r].sum + 1;
    a[k].w = a[a[k].l].w + a[a[k].r].w + a[k].x;
    if (a[k].l) a[a[k].l].fa = k;
    if (a[k].r) a[a[k].r].fa = k;
    a[k].fa = 0;
  }

  void pd(int k) {
    if (!a[k].tag) return;
    a[a[k].l].w += a[k].tag * a[a[k].l].sum;
    a[a[k].r].w += a[k].tag * a[a[k].r].sum;
    a[a[k].l].x += a[k].tag;
    a[a[k].r].x += a[k].tag;
    a[a[k].l].tag += a[k].tag;
    a[a[k].r].tag += a[k].tag;
    a[k].tag = 0;
  }

  void split(int k, int w, int &x, int &y) {
    if (!k) {
      x = y = 0;
      return;
    }
    pd(k);
    if (a[a[k].l].sum + 1 <= w) {
      x = k;
      int nk = a[k].r;
      split(nk, w - a[a[k].l].sum - 1, a[k].r = 0, y);
    } else {
      y = k;
      int nk = a[k].l;
      split(nk, w, x, a[k].l = 0);
    }
    update(k);
  }

  int merge(int x, int y) {
    if (!x || !y) return x + y;
    if (a[x].v < a[y].v) {
      pd(x), a[x].r = merge(a[x].r, y), update(x);
      return x;
    } else {
      pd(y), a[y].l = merge(x, a[y].l), update(y);
      return y;
    }
  }

  void Debug(int k) {
    if (!k) {
      return;
    }
    pd(k);
    Debug(a[k].l);
    cout << a[k].x << " ";
    Debug(a[k].r);
  }

  void Debug() {
    Debug(root);
    cout << endl;
  }

  void delet(int &k, int x) {
    int l = 0, r = 0, z = 0;
    split(k, x - 1, l, r);
    split(r, 1, r, z);
    k = merge(l, z);
  }

  void insert(int x, int val) {
    a[++tot] = {val, rand(), 0, 0, 0, 1, 0, val};
    int l = 0, r = 0;
    split(root, x, l, r);
    root = merge(merge(l, tot), r);
  }
} tr;

int n, m;

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  srand(time(NULL));
  cin >> n >> m;
  for (int i = 1, x; i <= n; i++) {
    cin >> x;
    tr.insert(i, x);
  }
  for (int op, x, y, k; m; m--) {
    cin >> op >> x >> y;
    if (op == 1) {
      cin >> k;
      int l = 0, m = 0, r = 0;
      tr.split(tr.root, y, l, r);
      tr.split(l, x - 1, l, m);
      tr.a[m].tag += k;
      tr.a[m].w += k * tr.a[m].sum;
      tr.a[m].x += k;
      tr.root = tr.merge(tr.merge(l, m), r);
    } else {
      int l = 0, m = 0, r = 0;
      tr.split(tr.root, y, l, r);
      tr.split(l, x - 1, l, m);
      cout << tr.a[m].w << endl;
      tr.root = tr.merge(tr.merge(l, m), r);
    }
  }
  return 0;
}

线段树版:

#include <iostream>

using namespace std;
using LL = long long;

const int MaxN = 1e5 + 10;

LL d[MaxN << 2], t[MaxN << 2], a[MaxN], n, m;

LL build(int l, int r, int p) {
  if (l == r) {
    return d[p] = a[l];
  }
  int mid = (l + r) >> 1;
  return d[p] = build(l, mid, p * 2) + build(mid + 1, r, p * 2 + 1);
}

void pushdown(int l, int r, int p, LL x) {
  t[p] += x, d[p] += x * (r - l + 1);
}

void G(int l, int r, int ll, int rr, int p, LL x) {
  if (ll <= l && r <= rr) {
    t[p] += x, d[p] += (r - l + 1) * x;
    return;
  }
  if (r < ll || l > rr) {
    return;
  }
  LL mid = (l + r) >> 1;
  pushdown(l, mid, p * 2, t[p]);
  pushdown(mid + 1, r, p * 2 + 1, t[p]);
  t[p] = 0;
  G(l, mid, ll, rr, p * 2, x), G(mid + 1, r, ll, rr, p * 2 + 1, x);
  d[p] = d[p * 2] + d[p * 2 + 1];
}

LL M(int l, int r, int ll, int rr, int p) {
  if (l > rr || r < ll) {
    return 0;
  }
  if (ll <= l && r <= rr) {
    return d[p];
  }
  LL mid = (l + r) >> 1;
  pushdown(l, mid, p * 2, t[p]);
  pushdown(mid + 1, r, p * 2 + 1, t[p]);
  t[p] = 0;
  return M(l, mid, ll, rr, p * 2) + M(mid + 1, r, ll, rr, p * 2 + 1);
}

int main() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  build(1, n, 1);
  for (LL op, l, r, x; m; m--) {
    cin >> op >> l >> r;
    if (op == 1) {
      cin >> x;
      G(1, n, l, r, 1, x);
    } else {
      cout << M(1, n, l, r, 1) << endl;
    }
  }
  return 0;
}

标签:binary,int,tr,ll,tree,tag,区间,数据,op
From: https://www.cnblogs.com/ybtarr/p/18086301

相关文章

  • ts_01_数据类型
    /***typeScript学习1数据类型*number数字类型*string字符串类型*boolean布尔类型*Array数组*Tuple元组*enum枚举*Any任意类型*void无任何类型*null空类型*undefined未定义类型*......
  • mysql 取最后一条数据的函数
    在MySQL中,要获取表中的最后一条数据,通常会使用ORDERBY子句结合LIMIT子句来实现。但是,如果您的表中没有明确的排序字段,或者想要获取实时的最后一条数据(例如,在插入新数据后),您可以使用LAST_INSERT_ID()函数,这个函数返回最后一个被插入的自增ID值。如果您的表设置了自增主键,那么在插......
  • 2023 年上半年数据库系统工程师考试
    基础知识●计算机中,系统总线用于(1)。(1)A.接口和外设​B.运算器、控制器和寄存器​C.CPU、主存及外设部件​D.DMA控制器和中断控制器参考答案:(1)C系统总线通常用来连接计算机中的各个部件(如CPU、内存和I/O设备)寄存器和运算器部件主要用片内总线连......
  • Oracle系列---【指定表指定字段数据同步】
    指定表指定字段数据同步1.把A库的A1表中的A11字段赋值给A12字段#把URL_NAME的值迁移到COMMENTS字段UPDATESYS_MENUSETCOMMENTS=URL_NAME;2.把A库的A1表中的A11字段赋值给B库的A1表中的A11字段UPDATECOM_SDM_FROMT.SYS_MENUFRSETFR.URL_NAME=(SELECTURL_NAME......
  • .NET快速实现网页数据抓取
    前言今天我们来讲讲如何使用.NET开源(MITLicense)的轻量、灵活、高性能、跨平台的分布式网络爬虫框架DotnetSpider来快速实现网页数据抓取功能。注意:为了自身安全请在国家法律允许范围内开发网页爬虫功能。网页数据抓取需求本文我们以抓取博客园10天推荐排行榜第一页的文章标......
  • 第三期【数据库主题文档上传激励活动】已开启!快来上传文档赢奖励
    2023年9月、11月,墨天轮社区相继举办了第一期与第二期【数据库主题文档上传激励活动】,众多用户积极参与、上传了大量优质的数据库主题干货文档,在记录经验的同时也为其他从业者带来了参考帮助,这正实现了“乐知乐享、共同成长”的活动初衷。为进一步壮大数据库资源“宝库”、向广大......
  • 阿里oceanbase数据库安装步骤-windows-docker
    打开阿里的安装教程:OceanBase分布式数据库-海量数据笔笔算数找到方案3:容器-docker。https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000639587 下载docker-desktop:https://www.docker.com/https://www.docker.com/products/docker-desktop/......
  • 数据清洗全流程总结
    #加载数据集data(airquality)#查看数据集str(airquality)head(airquality)查看NAcolSums(is.na(airquality))去除NAairquality_no_na<-na.omit(airquality)再次checkNAcolSums(is.na(airquality_no_na))查看duplicatesduplicated_rows<-duplicated(airqua......
  • 引燃算力新基建,天翼云亮相DCIC2024第13届数据中心产业发展大会!
    近日,由中国通信企业协会主办的“第13届数据中心产业发展大会暨AIDC智能算力生态合作展览会”在北京顺利举行。现场展示了天翼云“AIDC”“紫金”“云骁”“息壤”等技术和平台能力;中国电信天翼云2023年智算资源池上海节点建设工程获得大会“算力基础设施高质量发展企业案例奖”;天......
  • 使用链接服务器 从A数据库访问B数据库的表 或者建立视图
    通过SQLServer从A服务器访问B服务器表的方法场景:访问不同电脑上的数据库,且经常访问或数据量大,建议用链接服务器(位置:MicrosoftSQLServerManagementStudio->服务器对象->链接服务器)解决:1.创建链接服务器execsp_addlinkedserver'192.168.1.1','','SQLOLEDB','10.......