刷题记录:P8747 [蓝桥杯 2021 省 B] 双向排序 线段树

传送门:洛谷

题目描述

给定序列 ( a 1 , a 2 , ⋯   , a n ) = ( 1 , 2 , ⋯   , n ) \left(a_{1}, a_{2}, \cdots, a_{n}\right)=(1,2, \cdots, n) (a1,a2,,an)=(1,2,,n),即 a i = i a_{i}=i ai=i
小蓝将对这个序列进行 m m m 次操作,每次可能是将 a 1 , a 2 , ⋯   , a q i a_{1}, a_{2}, \cdots, a_{q_{i}} a1,a2,,aqi 降序排列,或者将 a q i , a q i + 1 , ⋯   , a n a_{q_{i}}, a_{q_{i}+1}, \cdots, a_{n} aqi,aqi+1,,an 升序排列。

输入:
3 3
0 3
1 2
0 2
输出:
3 1 2

我认为这道题是一道比较变态的题目

首先这道题有大概两种解法:
\quad\quad 一种解法是栈+思维,这种解法的思维难度较高,不是很好理解,也不太能在赛场上能想到,如果想把玩一下这种解法,那么网上有大量的题解,此处就不在介绍了

\quad\quad 我主要介绍另外一种解法.对于一段数列,我们发现这段数列中的所有数字要么处于下降数列中,要么处于上升区间中(这个很好理解).然后我们考虑维护每一个数字是处于下降数列中(用0表示),还是处于上升数列中(使用1表示).

\quad\quad 考虑使用 f e n j i e d i a n fenjiedian fenjiedian来记录上升数列开始的位置(因为显然是从下降序列过渡到上升序列中)

\quad\quad 初始化:刚开始显然所有数字都处于上升数列中,所以所有数字的状态都是1

\quad\quad 对于区间 [ 1 , q ] [1,q] [1,q]进行降序排序,那么此时假设我们的 q < f e n j i a n d i a n q<fenjiandian q<fenjiandian,那么此时我们的区间 [ 1 , q ] [1,q] [1,q]是已经是处于下降序列中,所以显然此时我们不需要对此进行排序; 假设此时我们的 q > = f e n j i e d i a n q>=fenjiedian q>=fenjiedian,那么此时我们发现我们是需要将区间 [ f e n j i a n d i a n , q ] [fenjiandian,q] [fenjiandian,q]从上升区间(也就是1状态)改为下降区间(也就是0状态).对于此时改的数字其实我们又有一点说法,因为对于每一段序列,我们的数字大小都是呈现出一个倒三角的形状,所以我们此时需要改变的数字就是所有数字状态为1的数字中从小到大选 q − f e n j i e d i a n + 1 q-fenjiedian+1 qfenjiedian+1个(此处需要细细理解!!).然后记得更改一下此时的 f e n j i e d i a n fenjiedian fenjiedian值即可.

\quad\quad 对于区间 [ 1 , q ] [1,q] [1,q]进行升序排序,此时我们做法和上述大致类似.只要找到所有状态为0的数字中从小到大选 f e n j i e d i a n − q + 1 fenjiedian-q+1 fenjiedianq+1个数字即可(原因和上述类似,因为改变的肯定是区间中最小的)然后将这些数字的状态从1改为0即可

\quad\quad 那么对于最终的答案来说,我们只要收集每一个数字的状态,对于状态为0的数字我们从大到小输出即可.对于状态为1的数字我们从小到大进行输出即可

对于区间修改,我们考虑使用线段树进行维护


下面是具体的代码部分(码量较大):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Segment_tree{
	int l,r;
	int sum,lazy;//sum记录1的个数
}tree[maxn*4];
void pushup(int rt) {
	tree[rt].sum=tree[ls].sum+tree[rs].sum;
}
void build(int l,int r,int rt) {//建树操作
	tree[rt].l=l;tree[rt].r=r;tree[rt].lazy=-1;
	if(l==r) {
		tree[rt].sum=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(rt);
}
void pushdown(int rt) {
	if(tree[rt].lazy==1) {
		tree[ls].sum=tree[ls].r-tree[ls].l+1;tree[rs].sum=tree[rs].r-tree[rs].l+1;
	}
	else {
		tree[ls].sum=tree[rs].sum=0;
	}
	tree[ls].lazy=tree[rt].lazy;tree[rs].lazy=tree[rt].lazy;
	tree[rt].lazy=-1;
}
void update0(int l,int r,int rt,int cnt) {//区间置0
	if(tree[rt].sum<=cnt) {
		tree[rt].sum=0;
		tree[rt].lazy=0;
		return ;
	}
	if(tree[rt].lazy!=-1) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	int lssum=tree[ls].sum;//注意这里
	if(tree[ls].sum>=cnt) update0(l,mid,ls,cnt);
	else update0(l,mid,ls,cnt),update0(mid+1,r,rs,cnt-lssum);
	pushup(rt);
}
void update1(int l,int r,int rt,int cnt) {//区间置1
	int lenrt=tree[rt].r-tree[rt].l+1;
	if(lenrt-tree[rt].sum<=cnt) {
		tree[rt].sum=tree[rt].r-tree[rt].l+1;
		tree[rt].lazy=1;
		return ;
	}
	if(tree[rt].lazy!=-1) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	int lenls=tree[ls].r-tree[ls].l+1;
	int lenrs=tree[rs].r-tree[rs].l+1;
	int lssum=lenls-tree[ls].sum;//注意这里
	if(lenls-tree[ls].sum>=cnt) update1(l,mid,ls,cnt);
	else update1(l,mid,ls,cnt),update1(mid+1,r,rs,cnt-lssum);
	pushup(rt);
}
int query(int pos,int rt) {//查询一个位置的数字状态
	if(tree[rt].l==pos&&tree[rt].r==pos) {
		return tree[rt].sum;
	}
	if(tree[rt].lazy!=-1) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(pos<=mid) return query(pos,ls);
	else return query(pos,rs);
}
int n,m;
int main() {
	n=read();m=read();
	int fenjiedian=1;//分界点
	build(root);
	for(int i=1;i<=m;i++) {
		int opt=read();
		if(opt==0) {
			int p=read();
			if(p<fenjiedian) continue;
			update0(1,n,1,p-fenjiedian+1);
			fenjiedian=p+1;
		}
		else {
			int q=read();
			if(q>=fenjiedian) continue;
			update1(1,n,1,fenjiedian-q);
			fenjiedian=q;
		}
	}
	vector<int>ans0;vector<int>ans1;
	for(int i=1;i<=n;i++) {
		if(query(i,1)) {
			ans1.push_back(i);
		}
		else {
			ans0.push_back(i);
		}
	}
	for(int i=ans0.size()-1;i>=0;i--) printf("%d ",ans0[i]);
	for(int i=0;i<ans1.size();i++) printf("%d ",ans1[i]);
	return 0;
}

类似题目推荐:P2824 [HEOI2016/TJOI2016]排序

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值