P4344 [SHOI2015] 脑洞治疗仪

[SHOI2015] 脑洞治疗仪

题目描述

曾经发明了自动刷题机的发明家 SHTSC 又公开了他的新发明:脑洞治疗仪——一种可以治疗他因为发明而日益增大的脑洞的神秘装置。

为了简单起见,我们将大脑视作一个 01 序列。 1 1 1 代表这个位置的脑组织正常工作, 0 0 0 代表这是一块脑洞。

1      0      1      0      0      0      1      1      1      0

脑洞治疗仪修补某一块脑洞的基本工作原理就是将另一块连续区域挖出,将其中正常工作的脑组织填补在这块脑洞中。(所以脑洞治疗仪是脑洞的治疗仪?)

例如,用上面第 8 8 8 号位置到第 10 10 10 号位置去修补第 1 1 1 号位置到第 4 4 4 号位置的脑洞,我们就会得到:

1      1      1      1      0      0      1      0      0      0

如果再用第 1 1 1 号位置到第 4 4 4 号位置去修补第 8 8 8 号位置到第 10 10 10 号位置:

0      0      0      0      0      0      1      1      1      1

这是因为脑洞治疗仪会把多余出来的脑组织直接扔掉。

如果再用第 7 7 7 号位置到第 10 10 10 号位置去填补第 1 1 1 号位置到第 6 6 6 号位置:

1      1      1      1      0      0      0      0      0      0

这是因为如果新脑洞挖出来的脑组织不够多,脑洞治疗仪仅会尽量填补位置比较靠前的脑洞。

假定初始时 SHTSC 并没有脑洞,给出一些挖脑洞和脑洞治疗的操作序列,你需要即时回答 SHTSC 的问题:在大脑某个区间中最大的连续脑洞区域有多大。

输入格式

第一行两个整数 n , m n,m n,m,表示 SHTSC 的大脑可分为从 1 1 1 n n n 编号的 n n n 个连续区域,有 m m m 个操作。

以下 m m m 行每行是下列三种格式之一:

  • 0 l r 0\quad l\quad r 0lr:SHTSC 挖了一个范围为 [ l , r ] [l, r] [l,r] 的脑洞。
  • 1 l 0 r 0 l 1 r 1 1\quad l_0\quad r_0\quad l_1\quad r_1 1l0r0l1r1:SHTSC 进行了一次脑洞治疗,用从 l 0 l_0 l0 r 0 r_0 r0 的脑组织修补 l 1 l_1 l1 r 1 r_1 r1 的脑洞。
  • 2 l r 2\quad l\quad r 2lr:SHTSC 询问 [ l , r ] [l, r] [l,r] 区间内最大的脑洞有多大。

上述区间均在 [ 1 , n ] [1, n] [1,n] 范围内。

输出格式

对于每个询问,输出一行一个整数,表示询问区间内最大连续脑洞区域有多大。

样例 #1

样例输入 #1

10 10
0 2 2
0 4 6
0 10 10
2 1 10
1 8 10 1 4
2 1 10
1 1 4 8 10
2 1 10
1 7 10 1 6
2 1 10

样例输出 #1

3
3
6
6

提示

对于 20 % 20\% 20% 的数据, n , m ≤ 100 n, m \leq 100 n,m100
对于 50 % 50\% 50% 的数据, n , m ≤ 20000 n, m \leq 20000 n,m20000
对于 100 % 100\% 100% 的数据, n , m ≤ 200000 n, m \leq 200000 n,m200000

题意:

给定一个 1 序列,有三种操作:

  • l , r l, r l,r : 将 区间 [ l , r ] [l,r] [l,r] 的序列置为 0
  • l 0 , r 0 , l 1 , r 1 l_0, r_0, l_1, r_1 l0,r0,l1,r1 : 将区间 [ l 0 , r 0 ] [l_0, r_0] [l0,r0] 中的 1 全部取出 并填充到 $[l_1, r_1] $ 中的 0 上。从左向右填充,且取出来的多余 1 扔掉
  • l , r l, r l,r : 询问 [ l , r ] [l, r] [l,r] 最长连续 0 的长度

解析:

因为询问是最长连续 0 的长度,所以可以考虑使用线段树来维护

对于操作一,线段树可以实现。

对于操作二,可以分为两步:

  • 取出 [ l 0 , r 0 ] [l_0, r_0] [l0,r0] 中的 1,并将这部分区间置为 0
  • 将取出来的 1 填充到 [ l 1 , r 1 ] [l_1, r_1] [l1,r1]

因为是从左到右填充,所以需要确定填充的位置 [ l 1 , p o s ] [l_1, pos] [l1,pos],即将 [ l 1 , p o s ] [l_1, pos] [l1,pos] 区间置为 1, ( p o s ≤ r 1 ) (pos \le r_1) (posr1)。因为 0 的个数具有单调性,所以可以通过二分来确定这个位置 p o s pos pos

时间复杂度为 O ( n l o g n + q l o g 2 n ) O(nlogn + qlog^2n) O(nlogn+qlog2n)

一些细节见代码

#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 2e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m;

inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}
struct node{
	int lmax, rmax; //脑洞,即0
	int sum; //0的个数
	int len; 
	int res; //最大连续0 
	int tag;
	void set(int x){
		lmax = rmax = sum = res = x;
	}
	node(){
		lmax = rmax = sum = res = tag = len = 0;
	}
}t[maxn << 2];
void pushup(node &k, node l, node r){
	k.sum = l.sum + r.sum;	
	k.lmax = l.lmax;
	if(l.len == l.sum)
		k.lmax = l.len + r.lmax;	
	k.rmax = r.rmax;
	if(r.len == r.sum)
		k.rmax = r.len + l.rmax;	
	k.res = max(l.res, r.res);
	k.res = max(k.res, l.rmax + r.lmax);	
}
void build(int k, int l, int r){
	t[k].len = r-l+1;
	t[k].tag = 0;
	if(l == r){
		t[k].set(0);
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(t[k], t[ls(k)], t[rs(k)]);
	return;
}

// tag = 0 : 无事发生
// tag = 1 : 全为脑洞 0
// tag = 2 : 全不脑洞 1 
void pushdown(int k){
	if(t[k].tag == 0)
		return;
		
	if(t[k].tag == 1){
		t[ls(k)].set(t[ls(k)].len);
		t[rs(k)].set(t[rs(k)].len);
	}
	else if(t[k].tag == 2){
		t[ls(k)].set(0);
		t[rs(k)].set(0);
	} 
	t[ls(k)].tag = t[rs(k)].tag = t[k].tag;
	t[k].tag = 0;
}

// v = 1 : 覆盖为脑洞
// v = 2 : 覆盖为脑组织 
void modify(int k, int l, int r, int x, int y, int v){
	if(x <= l && y >= r){
		t[k].tag = v;
		if(v == 1)
			t[k].set(t[k].len);
		else
			t[k].set(0); 
		return;
	}
	int mid = (l+r) >> 1;
	pushdown(k);
	if(x <= mid)
		modify(ls(k), l, mid, x, y, v);
	if(y > mid)
		modify(rs(k), mid+1, r, x, y, v);
	pushup(t[k], t[ls(k)], t[rs(k)]);
}

// 查询 1(脑组织) 的个数 
int querysum(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k].len - t[k].sum;
	int mid = (l+r) >> 1;
	pushdown(k);
	int ans = 0;
	if(x <= mid)
		ans += querysum(ls(k), l, mid, x, y);
	if(y > mid)
		ans += querysum(rs(k), mid+1, r, x, y);
	return ans;
}
void change(int l0, int r0, int l1, int r1){
	int cnt1 = querysum(1, 1, n, l0, r0);
	modify(1, 1, n, l0, r0, 1);
	
	if(cnt1 == 0) 
		return;
		
	int l = l1, r = r1, pos = l1;
	while(l <= r){
		int mid = (l+r) >> 1;
		int cnt0 = (mid-l1+1) - querysum(1, 1, n, l1, mid);
		if(cnt0 <= cnt1){
			pos = mid;
			l = mid + 1;
		}
		else
			r = mid - 1;
	}
	modify(1, 1, n, l1, pos, 2);
}
node query(int k, int l, int r, int x, int y){
	if(x <= l && y >= r){
		node x = t[k];
		return x;
	}
	pushdown(k);
	int mid = (l+r) >> 1;
	node lson, rson, ans;
	ans.len = r-l+1;
	if(x <= mid)
		lson = query(ls(k), l, mid, x, y);
	if(y > mid)
		rson = query(rs(k), mid+1, r, x, y);
	pushup(ans, lson, rson);
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	build(1, 1, n);
	while(m--){
		int op, l0, r0, l1, r1;
		cin >> op >> l0 >> r0;
		if(op == 0)
			modify(1, 1, n, l0, r0, 1);
		else if(op == 1){
			cin >> l1 >> r1;
			change(l0, r0, l1, r1);
		}			
		else if(op == 2){
			node qres = query(1, 1, n, l0, r0);
			cout << qres.res << endl;
		}
			
	}
	return 0;
}

你可能感兴趣的:(算法)