这一看都知道,肯定是线段树,只不过这个稍微有些复杂……
首先对于操作0和1都是很好办的,比较简单的区间修改。然后查询区间多少个1,就是区间和,也好办。
至于查询连续个1,做过酒店的都知道怎么办,维护一个imax[now](imax是interval's max,不是电影……):区间连续的1是多少个,lmax[now]:从now区间的左端点开始有多少个连续的1,rmax[now]从右端点开始有多少连续个1,这样区间合并的时候就是imax[now] = max(imax[now << 1], imax[now << 1 | 1], rmax[now << 1] + lmax[now << 1 | 1]).
查询酒店那道题没讲。对于要找的区间[L, R],如果各有一部分在当前区间的左右儿子中,那么分三种情况:1.在左儿子中,那么递归下去。1.在右儿子中,同样递归下去。3.最优解可能一部分在左儿子中,一部分在右儿子中,然而不能直接返回 t[now << 1].rmax +t[now << 1 |1].lmax,因为还有当前要找的区间的限制,因此是min(t[now << 1].rmax1, mid - L + 1) + min(t[now << 1| 1].lmax1, R - mid - 1 + 1)。然后这三种情况取max返回。
最后有解决的是区间反转问题,也是开一个标记(我就叫lzy_re了),他的优先级是比区间修改的优先级低的,因为若果一个区间都改成了0 / 1,那么这个区间之前的反转就没有用了,所以pushdown的时候,先把lazy往下传,再把lzy_re往下传。
这样的话要维护区间最长的1和0了,这样反转的时候只用把1和0维护的信息交换即可。
一个技巧就是在pushup的时候,可以重定义 '+' 号,这样用结构体的时候在结构体里面写就行,然后外面直接写一个 t[now] = t[now << 1] + t[now << 1 | 1] 就行了。
具体的细节看代码
1 #include<cstdio>
2 #include<iostream>
3 #include<algorithm>
4 #include<cmath>
5 #include<cstring>
6 #include<cstdlib>
7 #include<stack>
8 #include<queue>
9 #include<vector>
10 #include<cctype>
11 using namespace std;
12 #define enter puts("")
13 #define space putchar(' ')
14 #define Mem(a) memset(a, 0, sizeof(a))
15 typedef long long ll;
16 typedef double db;
17 const int INF = 0x3f3f3f3f;
18 const db eps = 1e-8;
19 const int maxn = 1e5 + 5;
20 inline ll read()
21 {
22 ll ans = 0;
23 char ch = getchar(), last = ' ';
24 while(!isdigit(ch)) {last = ch; ch = getchar();}
25 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
26 if(last == '-') ans = -ans;
27 return ans;
28 }
29 inline void write(ll x)
30 {
31 if(x < 0) putchar('-'), x = -x;
32 if(x >= 10) write(x / 10);
33 putchar(x % 10 + '0');
34 }
35
36 int n, m;
37
38 struct Tree
39 {
40 int l, r, sum, lzy, lzy_re;
41 int lmax0, rmax0, imax0, lmax1, rmax1, imax1;
42 Tree operator + (const Tree& other)const
43 {
44 Tree ret;
45 ret.l = l; ret.r = other.r; //这个别忘了
46 ret.sum = sum + other.sum;
47 ret.lzy = -1; ret.lzy_re = 0; //因为pushup操作在pushdown之后,所以此时所有标记已清空
48 ret.lmax0 = lmax0;
49 if(lmax0 == r - l + 1) ret.lmax0 += other.lmax0; //以下是维护区间最长的0和1,思路详见酒店那道题
50 ret.rmax0 = other.rmax0;
51 if(other.rmax0 == other.r - other.l + 1) ret.rmax0 += rmax0;
52 ret.imax0 = max(rmax0 + other.lmax0, max(imax0, other.imax0));
53 ret.lmax1 = lmax1;
54 if(lmax1 == r - l + 1) ret.lmax1 += other.lmax1;
55 ret.rmax1 = other.rmax1;
56 if(other.rmax1 == other.r - other.l + 1) ret.rmax1 += rmax1;
57 ret.imax1 = max(rmax1 + other.lmax1, max(imax1, other.imax1));
58 return ret;
59 }
60 }t[maxn << 2];
61
62 void build(int L, int R, int now)
63 {
64 t[now].l = L; t[now].r = R;
65 if(L == R)
66 {
67 t[now].sum = read(); t[now].lzy = -1; t[now].lzy_re = 0; //lzy初值最好设成-1,因为每一个数是0 / 1,初值为0不好判断
68 t[now].lmax0 = t[now].rmax0 = t[now].imax0 = t[now].sum ^ 1;
69 t[now].lmax1 = t[now].rmax1 = t[now].imax1 = t[now].sum;
70 return;
71 }
72 int mid = (L + R) >> 1;
73 build(L, mid, now << 1);
74 build(mid + 1, R, now << 1 | 1);
75 t[now] = t[now << 1] + t[now << 1 | 1];
76 }
77 void pushdown(int now)
78 {
79 if(t[now].lzy != -1) //先传区间修改
80 {
81 t[now << 1].sum = (t[now << 1].r - t[now << 1].l + 1) * t[now].lzy; //左区间
82 t[now << 1].lzy = t[now].lzy;
83 t[now << 1].lzy_re = 0; //他的左右儿子区间的反转标记得清零
84 t[now << 1].lmax0 = t[now << 1].rmax0 = t[now << 1].imax0 = t[now << 1].lzy ? 0 : t[now << 1].r - t[now << 1].l + 1;
85 //刚开始写成了 = t[now].sum * (t[now].lzy ^ 1),显然不对呀……
86 t[now << 1].lmax1 = t[now << 1].rmax1 = t[now << 1].imax1 = t[now << 1].sum;
87
88 t[now << 1 | 1].sum = (t[now << 1 | 1].r - t[now << 1 | 1].l + 1) * t[now].lzy; //右区间
89 t[now << 1 | 1].lzy = t[now].lzy;
90 t[now << 1 | 1].lzy_re = 0;
91 t[now << 1 | 1].lmax0 = t[now << 1 | 1].rmax0 = t[now << 1 | 1].imax0 = t[now << 1 | 1].lzy ? 0 : t[now << 1 | 1].r - t[now << 1 | 1].l + 1;
92 t[now << 1 | 1].lmax1 = t[now << 1 | 1].rmax1 = t[now << 1 | 1].imax1 = t[now << 1 | 1].sum;
93 t[now].lzy = -1;
94 }
95 if(t[now].lzy_re)
96 {
97 t[now << 1].sum = t[now << 1].r - t[now << 1].l + 1 - t[now << 1].sum;
98 t[now << 1].lzy_re ^= 1;
99 //注意一定要 ^ 1,而不是直接 = 1,因为很显然的是反转两次就还是原状。这就体现了lzy_re初值为0而不是-1的好处
100 swap(t[now << 1].lmax0, t[now << 1].lmax1); swap(t[now << 1].rmax0, t[now << 1].rmax1);
101 swap(t[now << 1].imax0, t[now << 1].imax1); //直接把维护最长的0和1这俩的信息交换就行
102
103 t[now << 1 | 1].sum = t[now << 1 | 1].r - t[now << 1 | 1].l + 1 - t[now << 1 | 1].sum;
104 t[now << 1 | 1].lzy_re ^= 1; //注意不是直接等于1
105 swap(t[now << 1 | 1].lmax0, t[now << 1 | 1].lmax1); swap(t[now << 1 | 1].rmax0, t[now << 1 | 1].rmax1);
106 swap(t[now << 1 | 1].imax0, t[now << 1 | 1].imax1);
107 t[now].lzy_re = 0;
108 }
109 }
110 void update_all(int L, int R, int now, bool d)
111 {
112 if(t[now].l == L && t[now].r == R)
113 {
114 t[now].sum = (R - L + 1) * d;
115 t[now].lzy = d;
116 t[now].lzy_re = 0;
117 t[now].lmax0 = t[now].rmax0 = t[now].imax0 = d ? 0 : R - L + 1;
118 t[now].lmax1 = t[now].rmax1 = t[now].imax1 = t[now].sum;
119 return;
120 }
121 pushdown(now);
122 int mid = (t[now].l + t[now].r) >> 1;
123 if(R <= mid) update_all(L, R, now << 1, d);
124 else if(L > mid) update_all(L, R, now << 1 | 1, d);
125 else update_all(L, mid, now << 1, d), update_all(mid + 1, R, now << 1 | 1, d);
126 t[now] = t[now << 1] + t[now << 1 | 1];
127 }
128 void update_re(int L, int R, int now)
129 {
130 if(t[now].l == L && t[now].r == R)
131 {
132 t[now].sum = R - L + 1 - t[now].sum;
133 t[now].lzy_re ^= 1;
134 swap(t[now].lmax0, t[now].lmax1); swap(t[now].rmax0, t[now].rmax1);
135 swap(t[now].imax0, t[now].imax1);
136 return;
137 }
138 pushdown(now);
139 int mid = (t[now].l + t[now].r) >> 1;
140 if(R <= mid) update_re(L, R, now << 1);
141 else if(L > mid) update_re(L, R, now << 1 | 1);
142 else update_re(L, mid, now << 1), update_re(mid + 1, R, now << 1 | 1);
143 t[now] = t[now << 1] + t[now << 1 | 1];
144 }
145 int query_sum(int L, int R, int now)
146 {
147 if(t[now].l == L && t[now].r == R) return t[now].sum;
148 pushdown(now);
149 int mid = (t[now].l + t[now].r) >> 1;
150 if(R <= mid) return query_sum(L, R, now << 1);
151 else if(L > mid) return query_sum(L, R, now << 1 | 1);
152 else return query_sum(L, mid, now << 1) + query_sum(mid + 1, R, now << 1 | 1);
153 }
154 int query_max(int L, int R, int now)
155 {
156 if(t[now].l == L && t[now].r == R) return t[now].imax1;
157 pushdown(now);
158 int mid = (t[now].l + t[now].r) >> 1;
159 if(R <= mid) return query_max(L, R, now << 1);
160 else if(L > mid) return query_max(L, R, now << 1 | 1);
161 else //最大值分三种情况
162 {
163 int ret1 = max(query_max(L, mid, now << 1), query_max(mid + 1, R, now << 1 | 1)); //分别在左右区间
164 int ret2 = min(t[now << 1].rmax1, mid - L + 1) + min(t[now << 1| 1].lmax1, R - mid - 1 + 1); //在两区间都有
165 //刚开始写成 min(t[now << 1].rmax1, L) + min(t[now << 1| 1].lmax1, R),因为比较的是长度,而L, R是端点,自然就不对了
166 return max(ret1, ret2);
167 }
168 }
169
170 int main()
171 {
172 n = read(); m = read();
173 build(0, n - 1, 1);
174 for(int i = 1; i <= m; ++i)
175 {
176 int d = read(), L = read(), R = read();
177 if(d == 0) update_all(L, R, 1, 0);
178 else if(d == 1) update_all(L, R, 1, 1);
179 else if(d == 2) update_re(L, R, 1);
180 else if(d == 3) write(query_sum(L, R, 1)), enter;
181 else write(query_max(L, R, 1)), enter;
182 }
183 return 0;
184 }
来源:oschina
链接:https://my.oschina.net/u/4281239/blog/3859389