[HEOI2016/TJOI2016]排序 解题报告

[HEOI2016/TJOI2016]排序

题意

给出一个大小为 \(n\) 的排列, 对这个排列进行 \(m\) 次操作, 操作分为以下两种,

  1. 0 l r 表示将区间 \([l,r]\) 的数升序排序.
  2. 1 l r 表示将区间 \([l,r]\) 的数降序排序.

询问 \(m\) 次操作后下标为 \(q\) 的数字.


思路

不看题解打死也想不出来系列

考虑二分答案.

设当前二分的答案为 \(mid\), 把原排列中 大于等于 \(mid\) 的数标记为 \(1\), 小于 \(mid\) 的数标记为 \(0\).

对于这样的一个 \(0,1\) 串, 我们可以用线段树实现 \(\log n\) 的排序.

若当前区间 \(l,r\) 的区间和为 \(num\), 那么就代表有 \(num\) 个 \(1\) 和 \(r-l+1-num\) 个 \(0\), 然后我们只需按要求把区间的前部分设为 \(0\) 或 \(1\), 后部分设为另一个数就行了.

最后判断一下下标为 \(q\) 的数字是否为 \(1\), 若是, 则另 \(l=mid+1\), 否则另 \(r=mid-1\).


代码

#include<bits/stdc++.h>
using namespace std;
const int _=1e5+7;
const int __=4e5+7;
int n,m,q,a[_],sum[__],tag[__],ans,x;
struct oper{
  int l,r,ty;
}op[_];
void init(){
  cin>>n>>m;
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
  for(int i=1;i<=m;i++)
    scanf("%d%d%d",&op[i].ty,&op[i].l,&op[i].r);
  cin>>q;
}
void build(int k,int l,int r){
  tag[k]=-1;
  if(l==r){ sum[k]= a[l]>=x; return; }
  int mid=(l+r)>>1;
  build(k<<1,l,mid);
  build(k<<1|1,mid+1,r);
  sum[k]=sum[k<<1]+sum[k<<1|1];
}
void upd(int k,int l,int r,bool w){
  sum[k]= w ?r-l+1 :0;
  tag[k]=w;
}
void psd(int k,int l,int r){
  if(tag[k]==-1) return;
  int mid=(l+r)>>1;
  upd(k<<1,l,mid,tag[k]);
  upd(k<<1|1,mid+1,r,tag[k]);
  tag[k]=-1;
}
void modify(int k,int l,int r,int x,int y,bool w){
  if(x>y) return;
  if(l>=x&&r<=y){ upd(k,l,r,w); return; }
  psd(k,l,r);
  int mid=(l+r)>>1;
  if(x<=mid) modify(k<<1,l,mid,x,y,w);
  if(y>mid) modify(k<<1|1,mid+1,r,x,y,w);
  sum[k]=sum[k<<1]+sum[k<<1|1];
}
int query(int k,int l,int r,int x,int y){
  if(l>=x&&r<=y) return sum[k];
  psd(k,l,r);
  int mid=(l+r)>>1,res=0;
  if(x<=mid) res+=query(k<<1,l,mid,x,y);
  if(y>mid) res+=query(k<<1|1,mid+1,r,x,y);
  return res;
}
bool judge(int mid){
  x=mid;
  build(1,1,n);
  for(int i=1;i<=m;i++){
    int num=query(1,1,n,op[i].l,op[i].r);
    if(!op[i].ty){
      modify(1,1,n,op[i].l,op[i].r-num,0);
      modify(1,1,n,op[i].r-num+1,op[i].r,1);
    }
    else{
      modify(1,1,n,op[i].l,op[i].l+num-1,1);
      modify(1,1,n,op[i].l+num,op[i].r,0);
    }
  }
  return query(1,1,n,q,q);
}
void run(){
  int l=1,r=n;
  while(l<=r){
    int mid=(l+r)>>1;
    if(judge(mid)){ ans=mid; l=mid+1; }
    else r=mid-1;
  }
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
  freopen("x.out","w",stdout);
#endif
  init();
  run();
  printf("%d\n",ans);
}

原文地址:https://www.cnblogs.com/brucew-07/p/12193650.html

时间: 2024-10-07 21:33:07

[HEOI2016/TJOI2016]排序 解题报告的相关文章

[HEOI2016/TJOI2016]排序

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec Memory Limit: 256 MB Submit: 2366 Solved: 1188 [Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,

[HEOI2016&amp;TJOI2016] 排序(线段树)

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2703  Solved: 1386[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,

BZOJ 3990 [SDOI 2015] 排序 解题报告

这个题哎呀...细节超级多... 首先,我猜了一个结论.如果有一种排序方案是可行的,假设这个方案是 $S$ . 那么我们把 $S$ 给任意重新排列之后,也必然可以构造出一组合法方案来. 于是我们就可以 $O(2^n)$ 枚举每个操作进不进行,再去判断,如果可行就 $ans$ += $|S|!$. 然而怎么判断呢? 我们按照操作种类从小到大操作. 假设我们现在在决策第 $i$ 种操作并且保证之前之后不需要进行种类编号 $< i$ 的操作. 那么我们只考虑那些位置在 $2^i+1$ 的位置的那些数.

[Luogu2824] [HEOI2016/TJOI2016]排序

题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字. 输入输出格式 输入格式: 输入数据的第一行为两个整数n和m.n表示序列的长度,m表示局部排序的次数.1 <= n, m <= 10^5第二

LGP2824【[HEOI2016/TJOI2016]排序】

一道神题ORZ,思路真的很妙啊. ### 正文部分: 题意: 给一个序列,可以对某一个区间升序和降序排序,问你最后数列中第$Q$个数是什么? 乍一看貌似毫无思路,于是我们考虑一个更简单的问题:如果对$1$个$01$序列执行上面的操作,是不是就可以变得简单一点? 设某段区间$[l,r]$里总共有$cnt$个1 那么降序排就是把$l\sim l+cnt - 1$修改为$1$,把$l+cnt \sim r$修改为$0$ 升序排则是把$r-cnt+1\sim r$修改为$1$,$l\sim r-cnt$

C 排序 解题报告

C 排序 题意 给一个\(1\sim n(n\le 10^6)\)排列,求这个排列用冒泡排序从小到大排序的第\(cnt\)步的状态.这里步的定义为,比较一次算一步. 贴个我看的不是很懂的题解,嗯不是很懂里面01到底咋玩了,如果有理解的比较清楚的教教我这个菜鸡吧. 说一下我自己的想法吧. 还是一轮一轮来,把\(p_i=i\)放好一次算一轮,先把整轮的给移动好 每一次把\(a_j=i\)放到\(i\)这个位置后,实际上是把\(a_i\)这个元素放到了右边第一个比\(a_i\)小的元素\(a_k\)的

【线段树合并】【P2824】 [HEOI2016/TJOI2016]排序

Description 给定一个长度为 \(n\) 的排列,有 \(m\) 次操作,每次选取一段局部进行升序或降序排序,问你一波操作后某个位置上的数字是几 Hint \(1~\leq~n,~m~\leq~10^5\) Solution 有两种做法,一种在线一种离线,这里把在线部分讲得更清楚点吧-- 考虑离线算法,我们二分该位置上的答案,将大于该数的元素置为 \(1\),小于该数的元素置为 \(0\),然后模拟所有的排序并检验.由于使用线段树对 \(0/1\) 序列多次局部排序可以做到 \(O(m

luogu_P2824 [HEOI2016/TJOI2016]排序

https://www.luogu.org/problem/P2824 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字. 输入格式 输入数据的第一行为两个整数n和m.n表示序列的长度,m表示

字符串排序解题报告

字符串排序 题目 思路及流程图 1.定义数组用于储存str[5][80] 2.输入输入字符串 3.用选择排序将字符串从小到大排列 4.按题目格式输出 流程图: 核心代码 问题 1.scanf中把所读入的字符串传递给str[i]即可 2.排序时不能直接将一个数组赋值给另一个数组,要调用strcpy函数 原文地址:https://www.cnblogs.com/ananChampion/p/12045555.html