[SCOI2010]序列操作 线段树
题解:
在考场上打的这道题,出人意料的很快就打完了?!
直接用线段树,维护几个东西:
1,lazy标记 : 表示区间赋值
2,mark标记:表示区间翻转
3,l1:前缀最长连续的1的子段长度
4,l0:前缀最长连续的0的子段长度
5,m0:区间内最长的全为0的子段的长度
6,r0:后缀最长连续的0的子段长度
7,r1:后缀最长连续的1的子段长度
8,m1:区间内最长的全为1的子段的长度
9,sum :区间和
维护起来比较繁琐,细节较多,但是都不难,是可以自己想出来的。
这里提一个可以忽略标记处理顺序的小技巧:
因为lazy标记是可以覆盖mark标记的,因此一个节点在得到lazy标记时,清空mark标记。
因为mark标记可以看做是直接对lazy标记进行翻转,因此如果一个节点已经有lazy标记,那么在打上mark标记时,可以选择不得到mark标记,而是直接对lazy标记进行修改。
因此不论是什么情况,mark标记和lazy标记都不会同时存在,也就不会有处理的先后顺序问题了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 101000 5 #define ac 800000 6 #define LL long long 7 int n, m, ans; 8 int s[AC]; 9 int sum[ac], lazy[ac], l1[ac], r1[ac], l0[ac], r0[ac], m1[ac], m0[ac], l[ac], r[ac]; 10 bool mark[ac]; 11 12 inline int read() 13 { 14 int x = 0;char c = getchar(); 15 while(c > '9' || c < '0') c = getchar(); 16 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 17 return x; 18 } 19 20 inline void upmin(int &a, int b) 21 { 22 if(b < a) a = b; 23 } 24 25 inline void upmax(int &a, int b) 26 { 27 if(b > a) a = b; 28 } 29 30 inline int Max(int a, int b) 31 { 32 if(a > b) return a; 33 else return b; 34 } 35 36 void update(int x)//更新信息 37 { 38 int ll = x * 2, rr = x * 2 + 1; 39 m1[x] = Max(m1[ll], m1[rr]); 40 upmax(m1[x], r1[ll] + l1[rr]); 41 m0[x] = Max(m0[ll], m0[rr]); 42 upmax(m0[x], r0[ll] + l0[rr]); 43 if(sum[rr] == r[rr] - l[rr] + 1) r1[x] = sum[rr] + r1[ll]; 44 else r1[x] = r1[rr]; 45 if(!sum[rr]) r0[x] = r[rr] - l[rr] + 1 + r0[ll]; 46 else r0[x] = r0[rr]; 47 if(sum[ll] == r[ll] - l[ll] + 1) l1[x] = sum[ll] + l1[rr]; 48 else l1[x] = l1[ll]; 49 if(!sum[ll]) l0[x] = r[ll] - l[ll] + 1 + l0[rr]; 50 else l0[x] = l0[ll]; 51 sum[x] = sum[ll] + sum[rr]; 52 } 53 54 void getlazy(int x, int go) 55 { 56 if(go == 1) 57 { 58 lazy[x] = 1, sum[x] = 0; 59 l1[x] = r1[x] = m1[x] = 0; 60 l0[x] = r0[x] = m0[x] = r[x] - l[x] + 1; 61 } 62 else if(go == 2) 63 { 64 lazy[x] = 2; 65 sum[x] = r[x] - l[x] + 1; 66 l1[x] = r1[x] = m1[x] = sum[x]; 67 l0[x] = r0[x] = m0[x] = 0; 68 } 69 mark[x] = 0;//区间赋值可以抵消区间反转 70 } 71 72 void getmark(int x) 73 { 74 mark[x] ^= 1;//翻转翻转标记 75 if(lazy[x]) getlazy(x, lazy[x] % 2 + 1); 76 else 77 { 78 sum[x] = r[x] - l[x] + 1 - sum[x]; 79 swap(l1[x], l0[x]), swap(r1[x], r0[x]); 80 swap(m1[x], m0[x]); 81 } 82 } 83 84 void pushdown(int x)//下传标记 85 { 86 if(lazy[x]) 87 { 88 int ll = x * 2, rr = ll + 1; 89 if(lazy[x] == 1)//change to 0 90 getlazy(ll, 1), getlazy(rr, 1); 91 else//change to 1 92 getlazy(ll, 2), getlazy(rr, 2); 93 lazy[x] = 0; 94 } 95 if(mark[x]) 96 { 97 int ll = x * 2, rr = ll + 1; 98 getmark(ll), getmark(rr); 99 mark[x] = 0; 100 } 101 } 102 103 void change(int x, int ll, int rr, int go)//区间修改 104 { 105 pushdown(x); 106 if(l[x] == ll && r[x] == rr) 107 { 108 if(go <= 2) getlazy(x, go); 109 else getmark(x); 110 return; 111 } 112 int mid = (l[x] + r[x]) >> 1; 113 if(rr <= mid) change(x * 2, ll, rr, go); 114 else if(ll > mid) change(x * 2 + 1, ll, rr, go); 115 else 116 { 117 change(x * 2, ll, mid, go); 118 change(x * 2 + 1, mid + 1, rr, go); 119 } 120 update(x); 121 } 122 123 void getsum(int x, int ll, int rr) 124 { 125 pushdown(x); 126 if(l[x] == ll && r[x] == rr) 127 { 128 ans += sum[x]; 129 return ; 130 } 131 int mid = (l[x] + r[x]) >> 1; 132 if(rr <= mid) getsum(x * 2, ll, rr); 133 else if(ll > mid) getsum(x * 2 + 1, ll, rr); 134 else 135 { 136 getsum(x * 2, ll, mid); 137 getsum(x * 2 + 1, mid + 1, rr); 138 } 139 } 140 141 void find(int x, int ll, int rr)//询问连续1的长度 142 { 143 pushdown(x); 144 if(l[x] == ll && r[x] == rr) 145 { 146 upmax(ans, m1[x]); 147 return ; 148 } 149 int mid = (l[x] + r[x]) >> 1; 150 if(rr <= mid) find(x * 2, ll, rr); 151 else if(ll > mid) find(x * 2 + 1, ll, rr); 152 else 153 { 154 int tmp = min(mid - ll + 1, r1[x * 2]) + min(rr - mid, l1[x * 2 + 1]); 155 find(x * 2, ll, mid); 156 find(x * 2 + 1, mid + 1, rr); 157 upmax(ans, tmp); 158 } 159 } 160 161 void build(int x, int ll ,int rr) 162 { 163 l[x] = ll, r[x] = rr; 164 if(l[x] == r[x]) 165 { 166 sum[x] = l1[x] = r1[x] = m1[x] = s[l[x]]; 167 if(!sum[x]) l0[x] = r0[x] = m0[x] = 1; 168 return ; 169 } 170 int mid = (ll + rr) >> 1; 171 build(x * 2, ll, mid); 172 build(x * 2 + 1, mid + 1, rr); 173 update(x); 174 } 175 176 void pre() 177 { 178 n = read(), m = read(); 179 for(R i = 1; i <= n; i ++) s[i] = read(); 180 } 181 182 void work() 183 { 184 int opt, a, b; 185 for(R i = 1; i <= m; i ++) 186 { 187 // printf("!!!%d\n", i); 188 opt = read(), a = read() + 1, b = read() + 1; 189 if(!opt) change(1, a, b, 1); 190 else if(opt == 1) change(1, a, b, 2); 191 else if(opt == 2) change(1, a, b, 3); 192 else if(opt == 3) 193 { 194 ans = 0; 195 getsum(1, a, b); 196 printf("%d\n", ans); 197 } 198 else if(opt == 4) 199 { 200 ans = 0; 201 find(1, a, b); 202 printf("%d\n", ans); 203 } 204 } 205 } 206 207 int main() 208 { 209 //freopen("operation.in", "r", stdin); 210 // freopen("operation.out", "w", stdout); 211 pre(); 212 build(1, 1, n); 213 work(); 214 //fclose(stdin); 215 //fclose(stdout); 216 return 0; 217 }