HEOI2016/TJOI2016 排序

Link

Diffculty

算法难度5,思维难度5,代码难度5

Description

给定一个 n n n 排列,要求支持两个操作:

  1. 对[L,R]升序排序
  2. 对[L,R]降序排序

m m m 个操作之后,会询问位置 q q q 上的值。

1 ≤ n , m , q ≤ 1 0 5 1\le n,m,q\le 10^5 1n,m,q105

Solution

线段树分裂/合并的板题(

这题有个非常好写的做法,考虑二分答案。

二分答案mid之后,所有<=mid的数都变成0,所有>mid的数都变成1。

那么两种排序就可以用线段树区间修改来实现了。

最后去check位置 q q q 上的值是0还是1,就可以调整二分边界了。

O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

顺带一提,这题我试行了我的训练计划,在30min内完成了写暴力和正解,对拍。

下面提供了std,baoli和gen 。

// std
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=1e5+5;
int n,m,q;
int a[N],opt[N],L[N],R[N];
int sum[N<<2],cg[N<<2],Len[N<<2];
inline void pushup(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];}
inline void pushdown(int rt){
	if(cg[rt]!=-1){
		sum[rt<<1]=cg[rt]*Len[rt<<1];
		cg[rt<<1]=cg[rt];
		sum[rt<<1|1]=cg[rt]*Len[rt<<1|1];
		cg[rt<<1|1]=cg[rt];
		cg[rt]=-1;
	}
}
inline void build(int rt,int l,int r,int v){
	cg[rt]=-1;
	Len[rt]=r-l+1;
	if(l==r){
		if(a[l]>v)sum[rt]=1;
		else sum[rt]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid,v);
	build(rt<<1|1,mid+1,r,v);
	pushup(rt);
}
inline void modify(int rt,int l,int r,int L,int R,int v){
	if(L<=l && r<=R){
		cg[rt]=v;
		sum[rt]=v*Len[rt];
		return;
	}
	pushdown(rt);
	int mid=(l+r)>>1;
    if(L<=mid)modify(rt<<1,l,mid,L,R,v);
	if(mid+1<=R)modify(rt<<1|1,mid+1,r,L,R,v);
	pushup(rt);
}
inline int query(int rt,int l,int r,int L,int R){
	if(L<=l && r<=R)return sum[rt];
	pushdown(rt);
	int mid=(l+r)>>1,ans;
	if(L<=mid && mid+1<=R)ans=query(rt<<1,l,mid,L,R)+query(rt<<1|1,mid+1,r,L,R);
	else if(L<=mid)ans=query(rt<<1,l,mid,L,R);
	else ans=query(rt<<1|1,mid+1,r,L,R);
	pushup(rt);
	return ans;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int i=1;i<=m;++i){opt[i]=read();L[i]=read();R[i]=read();}
	q=read();
	int l=0,r=n,mid;
	while(l<r){
		mid=(l+r)>>1;
		build(1,1,n,mid);
		for(int i=1;i<=m;++i){
			int num=query(1,1,n,L[i],R[i]);
			if(!opt[i]){
				if(num)modify(1,1,n,R[i]-num+1,R[i],1);
				if(num<R[i]-L[i]+1)modify(1,1,n,L[i],R[i]-num,0);
			}
			else{
				if(num)modify(1,1,n,L[i],L[i]+num-1,1);
				if(num<R[i]-L[i]+1)modify(1,1,n,L[i]+num,R[i],0);
			}
		}
		if(query(1,1,n,q,q))l=mid+1;
		else r=mid;
	}
	printf("%d\n",l);
	return 0;
}

// baoli
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=1e5+5;
int n,m,q;
int a[N];
inline bool cmp(int x,int y){return x>y;}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int i=1;i<=m;++i){
		int opt=read(),l=read(),r=read();
		if(!opt)sort(a+l,a+r+1);
		else sort(a+l,a+r+1,cmp);
	}
	q=read();
	printf("%d\n",a[q]);
	return 0;
}

//gen
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int N=1005;
int a[N];
int main(){
	srand(time(0));
	int n=1000,m=1000;
	printf("%d %d\n",n,m);
	for(int i=1;i<=n;++i)a[i]=i;
	random_shuffle(a+1,a+n+1);
	for(int i=1;i<=n;++i)printf("%d ",a[i]);
	putchar('\n');
	for(int i=1;i<=m;++i){
		int opt=rand()&1;
		int l=rand()%n+1,r=rand()%n+1;
		if(l>r)swap(l,r);
		printf("%d %d %d\n",opt,l,r);
	}
	int q=rand()%n+1;
	printf("%d\n",q);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值