首页 > 其他分享 >SS241115C. 排序(sort)

SS241115C. 排序(sort)

时间:2024-11-15 19:30:09浏览次数:1  
标签:sort ch int SS241115C que 排序 top define

SS241115C. 排序(sort)

题意

给你一个长度为 \(n\) 的序列 \(a\),每次操作对 \([1,\frac{n}{2}],[\frac{n}{2}+1,n]\) 进行归并排序。有 \(q\) 次询问,给出 \(t,x\),问进行 \(t\) 次操作后 \(a_x\) 的值。

思路

考虑一次操作发生了什么。

假设 \(x<y\),那么 \(x\) 和它后面的一坨都会排到 \(y\) 前面。

这启发我们把左右两个序列分成若干区间,满足每个区间的开头都比后面那一坨大,比下一个区间开头小。

把一个区间看做一个关于开头的整体,一次操作就是给这些区间排序,区间内部不变。

进行一次操作后,如果有一个块横跨了左右不分,就把这个块拆开,\(\le \frac{n}{2}\) 的为一部分,右边拆成若干块。

这个拆块的操作我们只会做 \(O(n)\) 次,因为拆完就不会合并回去了。

可以使用权值线段树维护。线段树下标表示开头的值,每次线段树二分找出横跨的块,如果没有那么排序就结束了,否则拆块,更新线段树。

因为拆块 \(O(n)\) 次,因此其实排序的操作最多也是 \(O(n)\) 次。

需要预处理每个位置后面第一个比它大的位置,这样才能保证拆一次块时间是 \(O(1)\) 的。

总时间复杂度 \(O(n \log n)\)。

code

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace struggle {
	#define isdigit(x) (x>='0'&&x<='9')
	#define gc getchar_unlocked
	#define pc putchar_unlocked
	template <typename T>
	void read(T &x) {
		x=0;
		char ch=gc();
		for(;!isdigit(ch);ch=gc()) ;
		for(;isdigit(ch);ch=gc()) x=(x<<3)+(x<<1)+(ch^48);
	}
	template <typename T>
	void write(T x,char ch) {
		static int st[40];
		int top=0;
		do {
			st[top++]=x%10;
			x/=10;
		}while(x);
		while(top) pc(st[--top]^48);
		pc(ch);
	}
	constexpr int N=2e5+7,M=1e6+7;
	int n,q,t;
	int a[N];
	int c[N];
	int x;
	struct node {
		int id,t,x;
	}b[M];
	bool cmp (node a,node b) { return a.t < b.t; }
	int ne[N];
	int que[N],r;
	int _upper_bound(int x) {
		if(a[que[1]]<x) return n+1;
		int L=1,R=r;
		while(L<R) {
			int mid=(L+R+1)>>1;
			if(a[que[mid]]>x) L=mid;
			else R=mid-1;
		}
		return que[L];
	}
	int ans[M];
	struct segtree {
		int tr[N<<2];
		void pushup(int u) { tr[u]=tr[u<<1]+tr[u<<1|1]; }
		void insert(int u,int l,int r,int x,int w) {
			if(l==r) {
				return tr[u]+=w, void(0);
			}
			int mid=(l+r)>>1;
			if(x<=mid) insert(u<<1,l,mid,x,w);
			else insert(u<<1|1,mid+1,r,x,w);
			pushup(u);
		}
		bool chai(int u,int l,int r,int k) {
			if(l==r) {
				if(tr[u]==k) return 0;
				int len=tr[u]-k;
				tr[u]=k;
				int y=c[l]+k;
				while(len) {
					int p=min(len,ne[y]-y); 
					insert(1,1,n,a[y],p);
					len-=p;
					y+=p;
				}
				return 1;
			}
			int mid=(l+r)>>1;
			bool ans=0;
			if(tr[u<<1]>=k) ans=chai(u<<1,l,mid,k);
			else ans=chai(u<<1|1,mid+1,r,k-tr[u<<1]);
			pushup(u);
			return ans;
		}
		int query(int u,int l,int r,int x) {
			if(l==r) {
				int p=c[l]+x-1;
				return a[p];
			}
			int mid=(l+r)>>1;
			if(tr[u<<1]>=x) return query(u<<1,l,mid,x);
			return query(u<<1|1,mid+1,r,x-tr[u<<1]);
		}
	}T;
	void main() {
		read(n),read(q);
		rep(i,1,n) read(a[i]), c[a[i]]=i;
		rep(i,1,q) {
			read(t),read(x);
			b[i]={i,t-1,x};
		}
		sort(b+1,b+q+1,cmp);
		per(i,n,1) {
			ne[i]=_upper_bound(a[i]);
			while(r&&a[que[r]]<a[i]) --r;
			que[++r]=i;
		}
		int k=1;
		while(k<=(n>>1)) {
			int p=min(ne[k],(n>>1)+1);
			T.insert(1,1,n,a[k],p-k);
			k=p;
		}
		while(k<=n) {
			int p=ne[k];
			T.insert(1,1,n,a[k],p-k);
			k=p;
		}
		int cnt=0;
		int m=0;
		while(m<q&&b[m+1].t==-1) ++m, ans[b[m].id]=a[b[m].x];
		while(m<q&&b[m+1].t==0) ++m, ans[b[m].id]=T.query(1,1,n,b[m].x);
		while(m<q) {
			++cnt;
			if(!T.chai(1,1,n,n>>1)) {
				while(m<q) {
					++m;
					ans[b[m].id]=T.query(1,1,n,b[m].x);
				}
				break;
			}
			while(b[m+1].t==cnt) ++m, ans[b[m].id]=T.query(1,1,n,b[m].x);
		}
		rep(i,1,q) write(ans[i],'\n');
	}
}
int main() {
	#ifdef LOCAL
	freopen("my.out","w",stdout);
	#else 
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	#endif
	struggle :: main();
}

标签:sort,ch,int,SS241115C,que,排序,top,define
From: https://www.cnblogs.com/liyixin0514/p/18547957

相关文章

  • 并行排序算法:双调排序
    引入有一个排列,你可以通过“比较并交换”这个操作将该排列排好序,即,每次选择一对数\((i,j)\),若\(a_i>a_j\)则交换,否则不交换。但是,你可以把多对\((i,j)\)放在一次操作里并行“比较并交换”,此时操作数记1,与数对的对数无关,但是每个\(i\)只能出现至多一次。要求操作数最小。......
  • bash sort 命令的用法
    给定一个test.txtbanana,2,8apple,2,7cherry,2,9banana,3,4cherry,3,5apple,3,10sort命令默认按照字典从左到右逐个字符依次从小到大排序,空格和制表符是默认域分隔符字典顺序就是基于Unicode字符编码的值来排序的默认排序:$cattest.txt|sortapple,2,7apple,3,10b......
  • qsort
    qsort快速排序函数1.qsort函数简介qsort是C标准库中的一个函数,用于对数组进行快速排序。它定义在头文件<stdlib.h>中。qsort函数的原型如下:voidqsort(void*base,size_tnum,size_tsize,int(*compar)(constvoid*,constvoid*));参数说明:base:指向要排序......
  • 归并排序的实现
    基本思想归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(DivideandConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。......
  • Web前端之汉字排序、sort与localeCompare的介绍、编码顺序与字典顺序的区别
    MENU使用字典顺序对汉字进行排序(不支持多音字)编码顺序和字典顺序的区别sort与localeCompare的介绍使用字典顺序对汉字进行排序(不支持多音字)不使用拼音库,利用JavaScript的localeCompare方法直接按汉字的字典序排序。localeCompare可以在比较字符串时指定语言及排......
  • 排序算法——冒泡排序
    目录一、冒泡排序的原理二、冒泡排序的过程三、代码实现总结一、冒泡排序的原理冒泡排序是一种简单的排序算法,它通过从左往右依次遍历,比较相邻元素的大小,并根据需要交换它们的位置来排序数据,以升序为例,这个过程类似空中的泡泡,重量大的往下沉,重量小的往上浮,从而得名冒泡......
  • 秒懂-----冒泡排序
    排序方法是一种重要的,基本的算法。我相信很多人学习的第一个排序算法就是冒泡排序。可是有很多人(包括作者本人)在开始学冒泡排序时,总是会有这种感觉------明明一听就懂,为何就是写不出代码!!归根结底时没有去深入它。何为冒泡生活中的冒泡请大家想象一口大锅,里面装满了水,现在......
  • C语言:数组(一维数组,二维数组,数组越界,数组作为函数参量,冒泡排序)
    1、一维数组的创建和初始化1.1、数组的创建数组是相同类型元素的集合•数组中可以存放1个或者多个数据•数组中存放的数据,类型是相同的数组的创建方式:元素类型自定义数组名(常量表达式)比如:intarr[10]doublearr[5]chararr[8+5]错误写法:intarr[n];......
  • Java常见排序算法详解:快速排序、插入排序与冒泡排序
    在程序设计中,排序是最基本的操作之一。Java提供了多种排序算法,今天我们将介绍三种常见的排序方法:快速排序、插入排序和冒泡排序。我们不仅会分析它们的基本原理,还会提供实际的代码实现,帮助大家更好地理解并应用这些排序算法。一、快速排序(QuickSort)快速排序是一种分治法的排......
  • 快速排序和归并排序的比较
    基本原理比较快速排序:基于分治策略。它选择一个基准元素(pivot),通过一趟排序将待排序序列分割成两部分,其中左边部分的元素都小于等于基准元素,右边部分的元素都大于等于基准元素。然后对这两部分分别进行快速排序,递归地重复这个过程,直到整个序列有序。例如,对于序列[4,7,......