题目描述
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 else12 {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 2 #include 3 #include 4 #include 5 #include 6 #include 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 else103 {104 int L = read(), add = read();105 update(L, L + add - 1, 1, d);106 }107 }108 return 0;109 }