BZOJ 4552 [Tjoi2016&Heoi2016]排序(二分答案 + 线段树)(给自己始终如一的提醒)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4552

在说二分之前,我觉得这道题ai的值域是<= n ,这不是就联想到权值线段树/树状数组了吗?(事实上也确实有这样的做法)
不过因为这是我听二分课的例题就果断二分了
如果二分答案的话,我们定义b[i] , 且a[i] > mid则b[i] = 1 ,否则为0,当然mid增加的话,b[i]为1的会减少,否则增大,满足二分的单调性
因此b[i]是个只有0和1的数组,我们想对其排序,不就是把该区间的1全部移到右边或左边吗?线段树可以解决区间覆盖问题(话说大家看到此题也立马觉得要用些有趣的数据结构了吧)
本题坑点(其实也不算很坑):
①由于线段树要被使用多次,所以注意清零
②(这应该是针对我自己的)我使用了双标记来解决覆盖问题,但是我在pushdown里面对于左右儿子只更新了它的一个标记,而未更新另一个标记,而在一些比较明显的地方我都注意过的,因此也WA了几遍

告诫:算法竞赛的代码要

始终如一!!!

AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
#define lid id << 1
#define rid (id << 1) | 1
#define rep(a,b,c) for (rg int a = 1 ; a <= c ; a += b)
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct tree{
	int l , r , max , sum , lazy1 , lazy2;
}t[maxn << 2];
int op[maxn] , x[maxn] , y[maxn] , a[maxn] , b[maxn];
void pushup(int id){
	t[id].sum = t[lid].sum + t[rid].sum;
}
void pushdown(int id){
	if (t[id].lazy1){
		t[lid].lazy1 = 1 , t[lid].lazy2 = 0 , t[lid].sum = t[lid].r - t[lid].l + 1;
		t[rid].lazy1 = 1 , t[rid].lazy2 = 0 , t[rid].sum = t[rid].r - t[rid].l + 1;
		t[id].lazy1 = 0;
	}
	if (t[id].lazy2){
		t[lid].lazy2 = 1 , t[lid].lazy1 = 0 , t[lid].sum = 0;
		t[rid].lazy2 = 1 , t[rid].lazy1 = 0 , t[rid].sum = 0;
		t[id].lazy2 = 0;
	}
}
void build(int id,int l,int r){
	t[id].l = l , t[id].r = r;
	t[id].lazy1 = t[id].lazy2 = 0;
	if (l == r) {t[id].sum = t[id].max = b[r];return;}
	rg int mid = (l + r) >> 1;
	build(lid , l , mid);
	build(rid , mid + 1 , r);
	pushup(id);
}
int query(int id,int l,int r){
	if (t[id].l == l && t[id].r == r){
		return t[id].sum;
	}
	pushdown(id);
	rg int mid = (t[id].l + t[id].r) >> 1;
	if (r <= mid) return query(lid , l , r);
	else if (l > mid) return query(rid , l , r);
	else {
		return query(lid , l , mid) + query(rid , mid + 1 , r);	
	}
}
void update1(int id,int l,int r){
	if (l > r) return;
	if (t[id].l == l && t[id].r == r){
		t[id].sum = t[id].r - t[id].l + 1; 
		t[id].lazy1 = 1 , t[id].lazy2 = 0;
		return;
	}
	pushdown(id);
	rg int mid = (t[id].l + t[id].r) >> 1;
	if (r <= mid) update1(lid , l , r);
	else if (l > mid) update1(rid , l , r);
	else {
		update1(lid , l , mid);update1(rid , mid + 1 , r);	
	}
	pushup(id);
}
void update2(int id,int l,int r){
	if (l > r) return;
	if (t[id].l == l && t[id].r == r){
		t[id].sum = 0;
		t[id].lazy1 = 0 , t[id].lazy2 = 1;
		return;
	}
	pushdown(id);
	rg int mid = (t[id].l + t[id].r) >> 1;
	if (r <= mid) update2(lid , l , r);
	else if (l > mid) update2(rid , l , r);
	else {
		update2(lid , l , mid);update2(rid , mid + 1 , r);	
	}
	pushup(id);
}
bool check(int mid , int n , int m , int q){
	for (rg int i = 1 ; i <= n ; ++i) if (a[i] > mid) b[i] = 1; else b[i] = 0;
	build(1 , 1 , n);
	for (rg int i = 1 ; i <= m ; ++i){
		rg int sumn = query(1 , x[i] , y[i]);
		if (!op[i]){
			update1(1 , y[i] - sumn + 1 , y[i]);
			update2(1 , x[i] , y[i] - sumn);	
		}else{
			update2(1 ,  x[i] + sumn , y[i]);
			update1(1 , x[i] , x[i] + sumn - 1);	
		}
	}
	return !query(1 , q , q);
}
int main(){
    rg int n = read() , m = read();
    for (rg int i = 1 ; i <= n ; ++i)
		a[i] = read();
	for (rg int i = 1 ; i <= m ; ++i)
		op[i] = read() , x[i] = read() , y[i] = read();	
	rg int q = read();
	rg int l = 1 , r = n;
	rg int ans;
	while (l <= r){
		rg int mid = (l + r) >> 1;
		if (check(mid , n , m , q)) ans = mid , r = mid - 1;
		else l = mid + 1;
	}	
	cout << ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值