题目描述
A城市有个超级大酒店,住房部只有一层,只向南,但房间有N个(编号1, 2 .. N)。刚开始所有房间都是空的,但酒店肯定会有人来住宿,有些房间就要被入住。但客人经常会问一个问题,最长连续的空房间是多少,因为客人想连续的住在一起。于是,有下列三个操作:
1 a b:表示从房间a开始连续b个房间,将被入住(不管有没有人已经入住)
2 a b:表示从房间a开始连续b个房间,将清空入住(不管有没有人入住)
3:表示询问当前最长连续的空房间是多少?
对于3的询问,输出相应答案。
输入
首先输入N和P, N如问题描述,P表示有多少个操作
然后输入P个操作
输出
对于第三种操作,输出相应答案
样例输入
12 10 3 1 2 3 1 9 4 3 2 2 1 3 2 9 2 3 2 3 2 3
样例输出
12 4 4 6 10
提示
【数据规模和约定】
3<=N<=16,000
3<=P<=200,000
首先,大家都能看出来,这是一道线段树的题。
那么除了常规的区记录间左右端点的数组,lazy数组,还要有一个数组sum[],表示所在区间内最长连续的空房间数。但是这并不够,在区间合并的时候很容易证明sum[now] = sum[now << 1] + sum[now << 1 | 1]是错的,这里不解释。
所以对于每一个区间,还要维护两个值:一个是从左端点开始最长连续空房间数lmax,另一个是从右端点开始最长连续空房间数rmax,举个栗子:
对于区间[1, 14]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# # # #
'#' 为已经入住的个房间
此时sum[now] = 6, lmax = 3, rmax = 1.
那么对于维护sum[now],就可以得出
sum[now] = max(sum[now << 1], sum[now << 1 | 1], rmax[now << 1] + lmax[now << 1 | 1])
也就是说要考虑连个子区间的空房间刚好连在一块的情况。
接下来就是想办法维护lmax[now]和rmax[now].
这个也不难(但我还是智障了debug了好长时间),想清楚了就行了。
拿lmax[now]举例,只要lmax[now << 1]是整个区间的长度的话,即now << 1整个区间都空,那么lmax[now] = lmax[now << 1] + lmax[now << 1 | 1],否则lmax[now] = lmax[now << 1].
rmax[now]同理。
最后想提一下lazy标记,和平时写的稍有些不同,不是累加的关系,而是将lazy[now]直接覆盖到lazy[now << 1]和lazy[now << 1 | 1]。因为对于一个房间只可能有入住和不入住两种情况,而这两种情况互不影响(题中描述如此:“不管有没有人已经入住”)。
所以更新的函数就写出来了。
更新,以及pushdown
1 void pushdown(int now)
2 {
3 if(!a[now].lazy) return;
4 a[now << 1].lazy = a[now << 1 | 1].lazy = a[now].lazy;
5 a[now].lazy = 0;
6 if(a[now].lazy == 2) //清空入住
7 {
8 a[now << 1].lmax = a[now << 1].rmax = a[now << 1].sum = a[now << 1].len;
9 a[now << 1 | 1].lmax = a[now << 1 | 1].rmax = a[now << 1 | 1].sum = a[now << 1 | 1].len;
10 }
11 else
12 {
13 a[now << 1].lmax = a[now << 1].rmax = a[now << 1].sum = 0;
14 a[now << 1 | 1].lmax = a[now << 1 | 1].rmax = a[now << 1 | 1].sum = 0;
15 }
16 }
17 void update(int L, int R, int now, int d)
18 {
19 if(L == a[now].l && R == a[now].r)
20 {
21 if(d == 2) a[now].lmax = a[now].rmax = a[now].sum = R - L + 1;
22 else a[now].lmax = a[now].rmax = a[now].sum = 0;
23 a[now].lazy = d; return; //直接覆盖lazy
24 }
25 pushdown(now);
26 int mid = (a[now].r + a[now].l) >> 1;
27 if(R <= mid) update(L, R, now << 1, d);
28 else if(L > mid) update(L, R, now << 1 | 1, d);
29 else {update(L, mid, now << 1, d); update(mid + 1, R, now << 1 | 1, d);}
30 a[now] = a[now << 1] + a[now << 1 | 1]; //因为用了结构体,所以这里重载了一下加号。
31 }
啊对了,补一下定义的结构体和重载运算符
struct Tree
{
int l, r, lazy, len; //len:区间长度
int sum, lmax, rmax;
/*其实这种封装也有一定的弊端,因为在重载加号的时候,返回的是一个新的结构体,
所以必须对他的所有成员都赋上相应的值,否则会出错,具体看下方注释 */
Tree operator + (const Tree& other)const
{
Tree ret;
ret.sum = max(max(sum, other.sum), rmax + other.lmax);
ret.l = l; ret.r = other.r;
ret.len = ret.r - ret.l + 1; //这就是容易疏忽的一点,我当时就忘了给len赋值,debug了1个多小时
if(lmax == len) ret.lmax = lmax + other.lmax; //分情况维护lmax,rmax
else ret.lmax = lmax;
if(other.rmax == other.len) ret.rmax = other.rmax + rmax;
else ret.rmax = other.rmax;
ret.lazy = 0; //因为这条语句是在pushdown之后执行的,所以lazy已经被清空了
return ret;
/*如此看来,结构体里就应该有sum,lmax,rmax,因为其他都是不变的,或是在pushup操作里不需要的
这么做也是尽量减少运算次数进行常数优化,但最重要的是,这样显得思维清晰,代码简练*/
}
}a[maxn << 2];
对于查询,只用返回a[1].sum了,因为是全局查询。
完整版代码
1 #include<cstdio>
2 #include<iostream>
3 #include<cstring>
4 #include<cmath>
5 #include<algorithm>
6 #include<cctype>
7 using namespace std;
8 #define enter printf("\n")
9 #define space printf(" ")
10 typedef long long ll;
11 const int INF = 0x3f3f3f3f;
12 const int maxn = 2e4 + 5;
13 inline ll read()
14 {
15 ll ans = 0;
16 char ch = getchar(), last = ' ';
17 while(!isdigit(ch)) {last = ch; ch = getchar();}
18 while(isdigit(ch))
19 {
20 ans = ans * 10 + ch - '0'; ch = getchar();
21 }
22 if(last == '-') ans = -ans;
23 return ans;
24 }
25 inline void write(ll x)
26 {
27 if(x < 0) x = -x, putchar('-');
28 if(x >= 10) write(x / 10);
29 putchar('0' + x % 10);
30 }
31
32 int n, p;
33
34 struct Tree
35 {
36 int l, r, lazy, len;
37 int sum, lmax, rmax;
38 Tree operator + (const Tree& other)const
39 {
40 Tree ret;
41 ret.sum = max(max(sum, other.sum), rmax + other.lmax);
42 ret.l = l; ret.r = other.r;
43 ret.len = ret.r - ret.l + 1;
44 if(lmax == len) ret.lmax = lmax + other.lmax;
45 else ret.lmax = lmax;
46 if(other.rmax == other.len) ret.rmax = other.rmax + rmax;
47 else ret.rmax = other.rmax;
48 ret.lazy = 0;
49 return ret;
50 }
51 }a[maxn << 2];
52 void build(int L, int R, int now)
53 {
54 a[now].l = L; a[now].r = R;
55 a[now].sum = a[now].lmax = a[now].rmax = a[now].len = R - L + 1;
56 a[now].lazy = 0;
57 if(L == R) return;
58 int mid = (L + R) >> 1;
59 build(L, mid, now << 1);
60 build(mid + 1, R, now << 1 | 1);
61 }
62 void pushdown(int now)
63 {
64 if(!a[now].lazy) return;
65 a[now << 1].lazy = a[now << 1 | 1].lazy = a[now].lazy;
66 a[now].lazy = 0;
67 if(a[now].lazy == 2)
68 {
69 a[now << 1].lmax = a[now << 1].rmax = a[now << 1].sum = a[now << 1].len;
70 a[now << 1 | 1].lmax = a[now << 1 | 1].rmax = a[now << 1 | 1].sum = a[now << 1 | 1].len;
71 }
72 else
73 {
74 a[now << 1].lmax = a[now << 1].rmax = a[now << 1].sum = 0;
75 a[now << 1 | 1].lmax = a[now << 1 | 1].rmax = a[now << 1 | 1].sum = 0;
76 }
77 }
78 void update(int L, int R, int now, int d)
79 {
80 if(L == a[now].l && R == a[now].r)
81 {
82 if(d == 2) a[now].lmax = a[now].rmax = a[now].sum = a[now].len;
83 else a[now].lmax = a[now].rmax = a[now].sum = 0;
84 a[now].lazy = d; return;
85 }
86 pushdown(now);
87 int mid = (a[now].r + a[now].l) >> 1;
88 if(R <= mid) update(L, R, now << 1, d);
89 else if(L > mid) update(L, R, now << 1 | 1, d);
90 else {update(L, mid, now << 1, d); update(mid + 1, R, now << 1 | 1, d);}
91 a[now] = a[now << 1] + a[now << 1 | 1];
92 }
93
94 int main()
95 {
96 n = read(); p = read();
97 build(1, n, 1);
98 for(int i = 1; i <= p; ++i)
99 {
100 int d = read();
101 if(d == 3) {write(a[1].sum); enter;}
102 else
103 {
104 int L = read(), add = read();
105 update(L, L + add - 1, 1, d);
106 }
107 }
108 return 0;
109 }