首页 > 其他分享 >ABC237G Range Sort Query

ABC237G Range Sort Query

时间:2023-06-06 22:11:27浏览次数:38  
标签:Sort std ABC237G res tree int Range 序列 assign

思路

这道题跟 P2824 的思路是很相似的。

首先由于我们只需求一个特定的值在排序后的位置,而原序列又是一个排列,因此我们可以将序列中的所有数分为三种:

  1. 大于 \(X\) 的;
  2. 等于 \(X\) 的;
  3. 小于 \(X\) 的。

我们不关心除了 \(X\) 之外的其他值的具体数字,而只关心其与 \(X\) 的大小关系,那么可以将数列抽象为一个 \(01\) 序列。将序列中所有大于等于 \(X\) 的数字赋为 \(1\),而小于 \(X\) 的数字赋为 \(0\)。然后利用线段树分类讨论即可以做到 \(O(n \log n)\) 模拟题目中的所有排序操作。

但是问题在于,题目是要求我们找到 \(X\) 这一个数字的具体位置,而上述的转化方法不支持我们找到 \(X\) 具体的位置,因为所有大于等于 \(X\) 的数字均对应 \(1\)。这该怎么办呢?

解决方法也很简单,再做一次就好了嘛!只不过这次,我们将所有小于等于 \(X\) 的值赋为 \(0\),大于 \(X\) 的值赋为 \(1\)。这样,\(X\) 在第一个序列中对应的是 \(1\) ,而在第二个序列中对应的是 \(0\),然后原序列又是一个排列,即只存在一个 \(X\),于是利用这个性质我们就可以找到 \(X\)。

那么具体的怎么用线段树来完成 \(01\) 序列的排序操作呢?很简单,由于只存在 \(01\) 两种数字,那么其实排序就是一个区间推平的问题。首先统计出带求区间中 \(1\) 的个数,如果不存在 \(1\) 我们直接跳过,如果存在的话,先把区间拍成 \(0\),然后若是升序就把所有的 \(1\) 丢到序列的末尾,若是降序就丢到开头,这也是可以直接用区间推平维护的。

那么总的时间复杂度就做到了 \(O(n \log n)\)。

代码

#include <bits/stdc++.h>

using i64 = long long;

struct SegTree {
  int n;
  struct Node { 
    int sum, tag, l, r; 
    Node() { sum = l = r = 0, tag = -1; }
  };
  friend Node operator+(const Node &lhs, const Node &rhs) {
    Node res;
    res.l = lhs.l, res.r = rhs.r, res.sum = lhs.sum + rhs.sum;
    return res;
  }
  std::vector<Node> tree;
  std::vector<int> a;
  SegTree(int _n) { n = _n, tree.resize((n << 2) + 1); }
  void build(int l, int r, int u) {
    if (l == r) {
      tree[u].l = l, tree[u].r = r, tree[u].sum = a[l - 1];
      return;
    }
    int mid = (l + r) >> 1, lc = u << 1, rc = u << 1 | 1;
    build(l, mid, lc), build(mid + 1, r, rc);
    tree[u] = tree[lc] + tree[rc];
  }
  void build(const std::vector<int> &_a) {
    a = _a;
    build(1, n, 1);
  }
  void tagging(int u, int val) {
    tree[u].sum = (tree[u].r - tree[u].l + 1) * val;
    tree[u].tag = val;
  }
  void pushdown(int u) {
    if (tree[u].tag == -1) { return; }
    tagging(u << 1, tree[u].tag), tagging(u << 1 | 1, tree[u].tag);
    tree[u].tag = -1;
  }
  void assign(int l, int r, int u, int val) {
    int s = tree[u].l, t = tree[u].r;
    if (l <= s && t <= r) {
      tagging(u, val);
      return;
    }
    pushdown(u);
    int mid = (s + t) >> 1, lc = u << 1, rc = u << 1 | 1;
    if (mid >= l) { assign(l, r, lc, val); }
    if (mid <  r) { assign(l, r, rc, val); }
    tree[u] = tree[lc] + tree[rc];
  }
  int query(int l, int r, int u) {
    int s = tree[u].l, t = tree[u].r;
    if (l <= s && t <= r) { return tree[u].sum; }
    pushdown(u);
    int mid = (s + t) >> 1, lc = u << 1, rc = u << 1 | 1;
    int res = 0;
    if (mid >= l) { res += query(l, r, lc); }
    if (mid <  r) { res += query(l, r, rc); }
    return res;
  }
};

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);

  int n, m, p;
  std::cin >> n >> m >> p;

  std::vector<int> a(n);
  for (int i = 0; i < n; i++) {
    std::cin >> a[i];
  }

  SegTree t1(n), t2(n);
  std::vector<int> b(n), c(n);
  for (int i = 0; i < n; i++) { b[i] = (a[i] >= p), c[i] = (a[i] > p); }
  t1.build(b), t2.build(c);
  
  for (int i = 0; i < m; i++) {
    int opt, l, r;
    std::cin >> opt >> l >> r;

    int cnt1 = t1.query(l, r, 1), cnt2 = t2.query(l, r, 1);
    t1.assign(l, r, 1, 0), t2.assign(l, r, 1, 0);
    if (opt == 2) {
      if (cnt1) { t1.assign(l, l + cnt1 - 1, 1, 1); }
      if (cnt2) { t2.assign(l, l + cnt2 - 1, 1, 1); }
    } else {
      if (cnt1) { t1.assign(r - cnt1 + 1, r, 1, 1); }
      if (cnt2) { t2.assign(r - cnt2 + 1, r, 1, 1); }
    }
  }

  for (int i = 1; i <= n; i++) {
    if ((!!t1.query(i, i, 1)) && (!t2.query(i, i, 1))) {
      std::cout << i << "\n";
      return 0;
    }
  }

  return 0;
}

标签:Sort,std,ABC237G,res,tree,int,Range,序列,assign
From: https://www.cnblogs.com/forgot-dream/p/17461885.html

相关文章

  • win10,vs2015深度学习目标检测YOLOV5+deepsort C++多目标跟踪代码实现,源码注释,拿来即
    int8,FP16等选择,而且拿来即用,自己再win10安装上驱动可以立即使用,不用在自己配置,支持答疑。自己辛苦整理的,求大佬打赏一顿饭钱。苦苦苦、平时比较比忙,自己后期会继续发布真实场景项目;欢迎下载。优点:1、架构清晰,yolov5和sort是分开单独写的,可以随意拆解拼接,都是对外接口。2、支持答疑......
  • Substring of Sorted String 题解
    SubstringofSortedString写篇题解纪念一下蒟蒻第一次赛时切出的F题。题目简述对一个字符串进行单点修改,区间判断操作。修改操作为将一个字符修改为另一个,判断操作为判断区间是否是整个字符串升序排序后的字串。思路分析蒟蒻第一眼线段树,但刚开始没仔细看题,以为是判断区......
  • sort命令
    -u去重-t指定分隔符-k指定取值的位置-r以相反的顺序来排序-n依照数值的大小排序-o将排序后的结果存入指定的文件-d按字典序升序排列,空行在前(默认)-h(2K、1G,根据此类大小来进行排序)-f忽略大小写进行排列......
  • 1839D - Ball Sorting (dp)
    题意:有一个1~n的序列,求放k个0后,最小操作次数,使得去掉0后序列升序,每次操作;可以把与0相邻的数,放到任意位置思路:因为n最大到500,并且求k属于1~n的所有最小代价,所以考虑dpdp[i][j],i表示以ai结尾放j个0的最小代价最小代价等于去掉以ai结尾升序列后,剩余子段用j个区间覆盖,的最小值......
  • range 类
    range类型相比常规list或tuple的优势在于一个range对象总是占用固定数量的(较小)内存,不论其所表示的范围有多大(因为它只保存了start,stop和step值,并会根据需要计算具体单项或子范围的值)。#classrange(object):#range(stop)->rangeobject#range(start,stop[,......
  • linux sort、uniq、tr、grep、eval、cut、sqlit、paste
    目录一、grep查找文件内容二、sort排序三、uniq统计压缩重复四、tr替换压缩 五、cut截断六.sqlit拆分七.paste合并八.eval        一、grep(匹配文件内容)    grep[选项]…查找条件目标文件-m 匹配次数-v  除什么以外......
  • 对话框变化大小后。CBCGPListCtrl、CListCtrl重新显示行数列数m_list_.Arrange(LVA_AL
    h文件中afx_msgvoidOnSize(UINTnType,intcx,intcy);voidResizeUI();vector<CRect>m_vec_rect_; BEGIN_MESSAGE_MAP(CDlgXXX,CBCGPDialog) ON_WM_SIZE() END_MESSAGE_MAP()BOOLCDlgXXX::OnInitDialog(){ CBCGPDialog::OnInitDialog(); EnableVisua......
  • Delphi RandomRange() - 返回指定范围内的随机整数
    DelphiRandomRange()-返回指定范围内的随机整数单元:math原型:functionRandomRange(constAFrom,ATo:Integer):Integer;beginifAFrom>ATothenResult:=Random(AFrom-ATo)+AToelseResult:=Random(ATo-AFrom)+AFrom;end;RandomRange......
  • 结构体排序 sort排序
    首先,在学习c的时候,应该学了很多排序方法吧,类似于冒泡排序呀,选择排序,插入排序,快排呀等等,但是,在c++中,有一个很好的排序就是sort排序,在stl里面,sort排序可以说,无论是时间复杂度还是空间复杂度,都是很优化的,这就足以见证sort排序的强大了,也说明sort排序的重要性。在C++中使用sort()函数......
  • qsort排序的用法
    //voidBubble_sort(intarr[],intsz)//{// inti=0;// for(i=0;i<sz-1;i++)//确定排序执行的次数// {// intj=0;// for(j=0;j<sz-1-i;j++)//确定每次排序两组元素的对比次数// {// inttmp=0;// if(arr[j]>arr[j+1])// {// tmp=......