首页 > 其他分享 >主席树

主席树

时间:2024-05-30 14:46:40浏览次数:13  
标签:tmp pr return int mid 主席 pl

引入

我们考虑对于一个长度为 \(n\) 的序列去找第 \(k\) 小,如果不用排序的话(虽然用了桶),可以利用一个桶匠所有数纪录下来,然后在桶上做二分即可(不会这个都不会吧),那么对于一个区间的话,我们便可以在区间 \([l, r]\) 上开桶然后做二分,不过这个桶我们该如何维护呢,首先我们想到前缀和,因为用前缀和可以快速求区间,可是如果直接用数组的话,我们无法直接快速的算出区间 \([l, r]\) 的桶,于是我们想到了平衡树的操作,将 \(r\) 处的平衡树把 \(l - 1\) 处的平衡树的一部分去点即可,不过想法很好,空间会炸,而且这很明显没用完美利用平衡树,所以考虑用一种可以将 \(n^2\) 空间换成一个小的空间的线段树。

思路

考虑到在这 \(n\) 次加点中,每次加点最多加一个点,而对于一颗线段树,只有一条链上的值发生了改变,从而我们将改变的数以及所在结构抽出来,形成一颗新的线段树,我们便用 \(log\) 的空间建了一个新的线段树,不过由于是在之前的基础上加的,正真在做这颗线段树的时候,是要算上前面的,所以我们需要将这棵树连回去,所以我们将这棵树的结构在原来位置上存在的边加回来,便是连上了,从而我们就成功的将 \(n ^ 2\) 空间缩小到了 \(nlogn\),那么在查询的时候,我们只需要考虑左边有多少个比它小的,所以只需要将前缀和的方式处理掉 \(l - 1\) 及以前有多少个数即可。

code

#include <iostream>

using namespace std;

const int MaxN = 2e5 + 10, inf = 1e9;

struct Node {
  int sum, l, r;
} a[MaxN << 5];

int rt[MaxN], n, m, tot;

void insert(int &x, int y, int l, int r, int v) {
  x = ++tot, a[x].sum = a[y].sum + 1;
  if (l == r) {
    return;
  }
  int mid = l + r >> 1;
  if (v <= mid) insert(a[x].l, a[y].l, l, mid, v), a[x].r = a[y].r;
  else insert(a[x].r, a[y].r, mid + 1, r, v), a[x].l = a[y].l;
}

int query(int x, int y, int l, int r, int k) {
  if (l == r) {
    return l;
  }
  int v = a[a[x].l].sum - a[a[y].l].sum, mid = l + r >> 1;
  if (k <= v) return query(a[x].l, a[y].l, l, mid, k);
  else return query(a[x].r, a[y].r, mid + 1, r, k - v);
}

int main() {
  cin >> n >> m;
  for (int i = 1, x; i <= n; i++) {
    cin >> x;
    insert(rt[i], rt[i - 1], 0, inf, x);
  }
  for (int i = 1, l, r, k; i <= m; i++) {
    cin >> l >> r >> k;
    cout << query(rt[r], rt[l - 1], 0, inf, k) << endl;
  }
  return 0;
}

P2839 [国家集训队] middle

当然,这个东西不一定是一个权值线段树

思路

由于要算中位数,所以我们先思考给了 \([l, r]\) 以及中位数 \(k\) 怎么保证这个是中位数,那么很显然将所有 \(< k\) 的改为 \(-1\),\(\ge k\) 的改为 \(1\),然后统计一下区间和是否 \(\ge 0\) 如果 \(\ge 0\) 就是可以不过可能可以再大些,否则就是大了,不成立,于是我们考虑二分去找中位数,那么对于会变动的区间又该如何呢?由于不考虑方案,所以我们只需要找方案里答案最大的即可,由于中间的 \([b + 1, c - 1]\) 必选,所以将整个变动区间分为三部分,中间直接求,两边取最大值即可,那我们不可能去暴力改变数组,所以可以用主席树维护当每一个点作为中位数时的每一个数的值。

code

#include <iostream>
#include <algorithm>

using namespace std;

const int MaxN = 40010, inf = 1e9;

struct Node {
  int x, l, r, lmax, rmax;
} a[MaxN << 5];

struct S {
  int x, y;

  bool operator<(const S &j) const {
    return x < j.x;
  }
} b[MaxN];

int rt[MaxN], n, m, tot;

void insert(int &x, int y, int k, int v, int l = 1, int r = n) {
  x = ++tot, a[x] = a[y];
  if (l == r) {
    a[x].x = v, a[x].lmax = a[x].rmax = v;
    return;
  }
  int mid = l + r >> 1;
  if (k <= mid)
    insert(a[x].l, a[y].l, k, v, l, mid);
  else
    insert(a[x].r, a[y].r, k, v, mid + 1, r);
  a[x].lmax = max(a[a[x].l].lmax, a[a[x].r].lmax + a[a[x].l].x);
  a[x].rmax = max(a[a[x].r].rmax, a[a[x].l].rmax + a[a[x].r].x);
  a[x].x = a[a[x].l].x + a[a[x].r].x;
}

int query(int x, int pl, int pr, int l = 1, int r = n) {
  if (pl <= l && r <= pr) {
    return a[x].x;
  }
  if (l > pr || r < pl) return 0;
  int mid = l + r >> 1;
  return query(a[x].l, pl, pr, l, mid) + query(a[x].r, pl, pr, mid + 1, r);
}

int queryl(int x, int pl, int pr, int l = 1, int r = n) {
  if (pl <= l && r <= pr) {
    return a[x].lmax;
  }
  int mid = l + r >> 1;
  if (mid >= pr) {
    return queryl(a[x].l, pl, pr, l, mid);
  } else if (mid < pl) {
    return queryl(a[x].r, pl, pr, mid + 1, r);
  }
  return max(queryl(a[x].l, pl, pr, l, mid), query(a[x].l, pl, pr, l, mid) + queryl(a[x].r, pl, pr, mid + 1, r));
}

int queryr(int x, int pl, int pr, int l = 1, int r = n) {
  if (pl <= l && r <= pr) {
    return a[x].rmax;
  }
  int mid = l + r >> 1;
  if (mid >= pr) {
    return queryr(a[x].l, pl, pr, l, mid);
  } else if (mid < pl) {
    return queryr(a[x].r, pl, pr, mid + 1, r);
  }
  return max(queryr(a[x].r, pl, pr, mid + 1, r), query(a[x].r, pl, pr, mid + 1, r) + queryr(a[x].l, pl, pr, l, mid));
}

bool Check(int x, int a, int b, int c, int d) {
  int cnt = 0;
  if (b + 1 <= c - 1) {
    cnt += query(rt[x], b + 1, c - 1);
  }
  cnt += queryr(rt[x], a, b) + queryl(rt[x], c, d);
  return cnt >= 0;
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> b[i].x, b[i].y = i;
  }
  sort(b + 1, b + n + 1);
  for (int i = 1; i <= n; i++) {
    insert(rt[i], rt[i - 1], b[i].y, 1);
  }
  rt[n + 1] = rt[n];
  for (int i = 2; i <= n; i++) {
    insert(rt[i + n], rt[i + n - 1], b[i - 1].y, -1);
  }
  cin >> m;
  for (int tmp[5] = {0}, x = 0; m; m--) {
    cin >> tmp[0] >> tmp[1] >> tmp[2] >> tmp[3];
    tmp[0] = (tmp[0] + x) % n + 1, tmp[1] = (tmp[1] + x) % n + 1, tmp[2] = (tmp[2] + x) % n + 1, tmp[3] = (tmp[3] + x) % n + 1;
    sort(tmp, tmp + 4);
    int l = 1, r = n;
    while (l < r) {
      int mid = l + r + 1 >> 1;
      Check(mid + n, tmp[0], tmp[1], tmp[2], tmp[3]) ? l = mid : r = mid - 1;
    }
    cout << b[l].x << endl;
    x = b[l].x;
  }
  return 0;
}

标签:tmp,pr,return,int,mid,主席,pl
From: https://www.cnblogs.com/ybtarr/p/18222287

相关文章

  • 学习笔记-主席树
    学习笔记-主席树主席树,就是可持久化权值线段树,也叫函数式线段树引入考虑如下问题:给定一个数列,查询其中第k大值显然,我们可以建一棵权值线段树,直接在上面二分就好了,即对于每个结点,查看它左子树的结点数量是否大于k,设为\(sum\)如果\(sum\gek\),则第k个结点在其左子树中,否则......
  • hdu4417(权值离散化后建立主席树)
    Problem-4417(hdu.edu.cn)马里奥是举世闻名的水管工。他“魁梧”的身材和惊人的跳跃能力让我们想起了。现在可怜的公主又遇到了麻烦,马里奥需要拯救他的爱人。我们将通往老板城堡的道路视为一条线(长度为n),在每个整数点i上都有一块高度为hi的砖。现在的问题是,如果马里奥可......
  • hdu4348(主席树区间修改)
    Problem-4348(hdu.edu.cn)BackgroundToTheMoon是一款独立游戏,于2011年11月发布,是一款由RPGMaker提供支持的角色扮演冒险游戏。《去月球》的前提是基于一种技术,该技术使我们能够永久地重建垂死之人的记忆。在这个问题上,我们将给你一个机会,实现幕后的逻辑。您已经获得了N......
  • 算法随笔——主席树(可持久化线段树)
    介绍主席树即可持久化线段树,由hjt大佬发明。好像又称函数式线段树。可以查询序列在任意时间的历史状态。学习链接学习链接主要思路维护历史状态,若采用直接拷贝整棵树的方式,空间爆炸。可以发现每次修改只有部分节点发生了改变(其实就是到树根那条链会改变),因此每次只需要记......
  • 主席树的简要讲解
    区间第k小值主席树是解决动态查找序列上[l,r]区间中的第k小值的一个数据结构核心思想:动态开点(后面会讲)传统线段树都是值域线段树其实意思就是每个节点都存的是序列上[l,r]的一个区间和,但是考虑我们需要动态处理区间的不是最值,故换一种线段树主席树一般用的是权值线段树就是把[......
  • 「主席树维护高精度」 CF464E The Classic Problem
    发现难点在于维护\(dis_u\)(起点\(s\)到\(u\)的最短路)。如果使用暴力高精度,复杂度会乘上一个\(len\)。考虑边权\(2^w\)的特殊性质,如果使用一个二进制串来维护\(dis\),那么加上边权\(2^w\)相当于将二进制下某一段\(1\)置为\(0\),然后再将一个\(0\)置为\(1\)。说白了......
  • 主席树的板子题
    题解方法1.可持久化线段树(主席树),代码有详细注释做法:先把值离散化在数值上建立线段树,维护每个数值区间中一共有多少个数问题1:如何求整体第K小数?答:二分,如果0~mid中有cnt数,cnt>=k,递归左边,如果cnt<k,递归右边,找k−cnt小的数。时间复杂的logn问题2:求【1,R】这个区间的第K......
  • 2024 ICPC Asia Pacific Championship-K-线段树合并or主席树
    比赛链接:https://codeforces.com/contest/1938给一棵有根树,执行以下代码:letLbeanemptyarrayforx=1ton fory=1ton append((x-1)*n*n+(LCA(x,y)-1)*n+(y-1))toLsortLinnon-decreasingorder然后进行\(q\)次询问,每次问\(L\)中第......
  • 主席树
    主席树定义可持久化的线段树实现voidmkrt(int&p,intq){inttmp=mknode();t[tmp]=t[q];p=tmp;}voidpushup(intp){t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;}voidmodify(int&p,intL,intR,intx,intv){if(!p)p=mknode();if(......
  • 主席树(可持久化线段树)
    主席树前言主席树也是一种数据结构,是线段树的进阶,作用是可以保留历史版本,支持回退,就是回到之前某个未修改的状态。就像我们在写博客时如果误删了重要的内容,就可以用Ctrl+z撤回一次操作,也可以用Ctrl+Shift+z还原我们撤回的操作,这就需要存下每次编辑的操作。基本原理可......