[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
", 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
", ans);
197         }
198         else if(opt == 4)
199         {
200             ans = 0;
201             find(1, a, b);
202             printf("%d
", 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 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9614689.html