CodeForces 631C Report「思维题」

题目描述

题意翻译

给定 \(n\) 个整数组成的序列和 \(m\) 个操作,每个操作给定 \(t_i\)\(r_i\),操作分为两种:

  • \(t_i\)\(1\),则将前 \(r_i\) 个数按照连续不减序(从小到大)排列;
  • \(t_i\)\(2\),则将前 \(r_i\) 个数按照连续不增序(从大到小)排列。

\(m\) 次操作过后的序列。

输入输出样例

输入 #1

3 1
1 2 3
2 2

输出 #1

2 1 3 

输入 #2

4 2
1 2 4 3
2 3
1 2

输出 #2

2 4 1 3 

思路分析

疫情期间把这题水过了,突然想回来补锅

  • 题面翻译的非常简洁明了又准确。不知道有多少人像我一样只优化了一丢丢就开始sort
  • 然后应该基本上都会想到这个很显然的性质:如果前面的修改操作的右端点小于后面操作的右端点,那么这次操作其实就是无效的,因为会被覆盖掉
  • 所以就很容易挑选出有效的操作次数,而被筛选操作是这样的:编号越小的修改所覆盖的区间越大,也就是覆盖区间的长度是单调递减的。如果不满足单调递减,那么前面一定会有操作会被覆盖。
  • 那么这个性质怎么用?其实上面说了,就是因为 sort 太多了所以 \(TLE\) 掉了,而这时候在这个性质下,完全不必 sort,而是可以直接赋值的。方法就是先排序,然后两次被挑选出的操作的端点之间直接赋值。在上面的性质的下,这样的正确性是有保证的

详见代码

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 200010
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,a[N],c[N],flag[N];
struct data{
	int opt,pos;
}b[N];
inline bool cmp(int x,int y){return x>y;}
int main(){
	n = read(),m = read();
	for(R int i = 1;i <= n;i++)a[i] = read();
	for(R int i = 1;i <= m;i++){
		b[i].opt = read(),b[i].pos = read();
	}
	int mx = 0;
	for(R int i = m;i;i--){//倒序处理出每次操作的后面的操作覆盖的最大区间
		flag[i] = mx;
		mx = max(mx,b[i].pos);
	}
	for(R int i = 1;i <= mx;i++)c[i] = a[i];//c数组用于下面原数组的赋值
	sort(c+1,c+1+mx,cmp);
	int l = 1,r = mx;
	for(R int i = 1;i <= m;i++){
		if(flag[i]>=b[i].pos)continue;
		if(b[i].opt==1){
			for(R int j = 0;b[i].pos-j>flag[i];j++){//从右端点开始赋值,一直到下一次操作的端点
				a[b[i].pos-j] = c[l++];//从大的开始拿
			}
		}else{
			for(R int j = 0;b[i].pos-j>flag[i];j++){
				a[b[i].pos-j] = c[r--];//从小的开始拿
			}
		}
	}
	for(R int i = 1;i <= n;i++)printf("%d ",a[i]);
	return 0;
}
posted @ 2020-10-12 15:07  HH_Halo  阅读(141)  评论(0编辑  收藏  举报