HDU 3911 Black and White (线段树,区间翻转)
【题目地址】
【题目大意】
- 海滩上有一堆石头。 石头的颜色是白色或黑色。 小肥羊拥有魔术刷,她可以改变连续石的颜色,从黑变白,从白变黑。 小肥羊非常喜欢黑色,因此她想知道范围[i,j]中连续的黑色石头的最长时间。
- 有多种情况,每种情况的第一行是整数n(1 <= n <= 10 ^ 5),后跟n个整数1或0(1表示黑石头,0表示白石头),然后是整数 M(1 <= M <= 10 ^ 5)后跟M个运算,格式为xij(x = 0或1),x = 1表示更改范围[i,j]中的石头颜色,并且x = 0表示询问 [i,j]范围内连续黑宝石的最长时间
- 当x = 0输出时,数字表示范围为[i,j]的黑色宝石的最长长度。
【样例输入】
4
1 0 1 0
5
0 1 4
1 2 3
0 1 4
1 3 3
0 4 4
【样例输出】
1
2
0
【一句话题意】
- 操作指令为0:
查询【L, R】中最长的连续的1的个数
- 操作指令为1:
将区间【L, R】中的0和1翻转
【难点】
没办法一步到位求得连续1的个数,如果强买强卖,就是暴力,我们的良心会受到谴责
【突破】逆推,运用分治的思想化繁为简
1.我们想要维护区间最长连续1的个数,只需要知道每个子区间内最大前缀1,
最大后缀1(原因先自己思考下)
2.想要维护答案和上述两个后缀长度,并且包含修改操作,那么可以去维护对应的0的个数,
即区间最长连续0的个数,区间最大前缀0,最大后缀0
3.众所周知,区间修改需要lazytag(懒标记)
4.(此时没明白不打紧,先往后看)
【思路梳理】
一、需要维护的7个变量:
区间最大前缀1
区间最大前缀0
区间最大后缀1
区间最大后缀0
区间最大连续1
区间最大连续0
懒惰标记
二、修改操作
向下更改时交换所有0,1相关值
三、查询
分割区间
计算左子区间最大后缀1与当前区间最大前缀1
计算右子区间最大前缀1与当前区间最大后缀1
将2、3条取max即为所求
四、注意细节,代码实现
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define int long long using namespace std; inline int read(){ int x = 0, w = 1; char ch = getchar(); for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * w; } const int maxn = 1000010; int a[maxn]; struct node{ int l0, r0; int l1, r1; int max0, max1; int lazytag; }tree[maxn << 2]; inline void solve(int u, int l, int r){//维护7个相关值 int mid = (l + r) >> 1; //前缀1 tree[u].l1 = tree[u << 1].l1; if(tree[u].l1 == mid - l + 1){ tree[u].l1 += tree[u << 1 | 1].l1; } //前缀0 tree[u].l0 = tree[u << 1].l0; if(tree[u].l0 == mid - l + 1){ tree[u].l0 += tree[u << 1 | 1].l0; } //后缀1 tree[u].r1 = tree[u << 1 | 1].r1; if(tree[u].r1 == r - mid){ tree[u].r1 += tree[u << 1].r1; } //后缀0 tree[u].r0 = tree[u << 1 | 1].r0; if(tree[u].r0 == r - mid){ tree[u].r0 += tree[u << 1].r0; } //区间最大1,最大0 tree[u].max1 = max (max (tree[u << 1].max1, tree[u << 1 | 1].max1), tree[u << 1].r1 + tree[u << 1 | 1].l1); tree[u].max0 = max (max (tree[u << 1].max0, tree[u << 1 | 1].max0), tree[u << 1].r0 + tree[u << 1 | 1].l0); } inline void SWAP(int u){//保留备用,每次做区间翻转的时候要用 swap(tree[u].l0, tree[u].l1); swap(tree[u].r0, tree[u].r1); swap(tree[u].max0, tree[u].max1); } inline void pushdown(int u, int l, int r){//懒标记下防 if(l != r){ tree[u << 1].lazytag ^= 1; tree[u << 1 | 1].lazytag ^= 1; SWAP(u << 1); SWAP(u << 1 | 1); tree[u].lazytag = 0; } } int tmp; inline void build(int u, int l, int r){//建树 tree[u].lazytag = 0; if(l == r){ tmp = read(); if(tmp == 1){//若当前石头为黑色 tree[u].l1 = tree[u].r1 = tree[u].max1 = 1; tree[u].l0 = tree[u].r0 = tree[u].max0 = 0; } else{//为白色 tree[u].l1 = tree[u].r1 = tree[u].max1 = 0; tree[u].l0 = tree[u].r0 = tree[u].max0 = 1; } return; } int mid = (l + r) >> 1; build(u << 1, l, mid);//左子树 build(u << 1 | 1, mid + 1, r);//右子树 solve(u, l, r);//维护7个相关值 } inline void change(int u, int l, int r, int s, int t){//区间翻转 if(l >= s && r <= t){//更新lazytag tree[u].lazytag ^= 1; SWAP(u); return; } if(tree[u].lazytag) pushdown(u, l, r); int mid = (l + r) >> 1; if(mid >= t) update(u << 1, l, mid, s, t); else if(mid < s) update(u << 1 | 1, mid + 1, r, s, t); else{ update(u << 1, l, mid, s, t); update(u << 1 | 1, mid + 1, r, s, t); } solve(u, l, r);//维护7个相关值 } inline int query(int u, int l, int r, int s, int t){ if(l >= s && r <= t){ return tree[u].max1; } if(tree[u].lazytag) pushdown(u, l ,r); int mid = (l + r) >> 1; if(mid >= t) return query(u << 1, l, mid, s, t); else if(mid < s) return query(u << 1 | 1, mid + 1, r, s, t); else{ int cnt1 = query(u << 1, l, mid, s, t); int cnt2 = query(u << 1 | 1, mid + 1, r, s, t); int cnt3 = min(mid - s + 1, tree[u << 1].r1); int cnt4 = min(t - mid, tree[u << 1 | 1].l1); return max(max(cnt1, cnt2), cnt3 + cnt4); } solve(u, l, r); } signed main(){ int n; while(scanf("%lld", &n) == 1){ build(1, 1, n); int m = read(); while(m--){ int opt = read(), l = read(), r = read(); if(opt == 1)//翻转 change(1, 1, n, l, r); else//查询 printf("%lld\n", query(1, 1, n, l, r)); } } return 0; }
风吹过,我来过~