Sequence operation
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
lxhgww got a sequence contains n characters which are all '0's or '1's.
We have five operations here:
Change operations:
0 a b change all characters into '0's in [a , b]
1 a b change all characters into '1's in [a , b]
2 a b change all '0's into '1's and change all '1's into '0's in [a, b]
Output operations:
3 a b output the number of '1's in [a, b]
4 a b output the length of the longest continuous '1' string in [a , b]
We have five operations here:
Change operations:
0 a b change all characters into '0's in [a , b]
1 a b change all characters into '1's in [a , b]
2 a b change all '0's into '1's and change all '1's into '0's in [a, b]
Output operations:
3 a b output the number of '1's in [a, b]
4 a b output the length of the longest continuous '1' string in [a , b]
Input
T(T<=10) in the first line is the case number.
Each case has two integers in the first line: n and m (1 <= n , m <= 100000).
The next line contains n characters, '0' or '1' separated by spaces.
Then m lines are the operations:
op a b: 0 <= op <= 4 , 0 <= a <= b < n.
Each case has two integers in the first line: n and m (1 <= n , m <= 100000).
The next line contains n characters, '0' or '1' separated by spaces.
Then m lines are the operations:
op a b: 0 <= op <= 4 , 0 <= a <= b < n.
Output
For each output operation , output the result.
Sample Input
1 10 10 0 0 0 1 1 0 1 0 1 1 1 0 2 3 0 5 2 2 2 4 0 4 0 3 6 2 3 7 4 2 8 1 0 5 0 5 6 3 3 9
Sample Output
5 2 6 5
————————————————————丢人的分割线————————————————————
前言:一道题做了这么多天。是丢人。物理补考也挂了,要交钱重修了。
思路:这道题是把几道线段树区间操作的题目合起来了。一个是POJ-3225 Help with Intervals一个是POJ-3667 Hotel一个是HDU-1698 Just a Hook。最基本的懒惰标记求区间和,成段替换以及区间合并。三种操作都要实现。代码能力渣的话要做很久才能做对。真是一道综合题。
对build、up、down操作都是只要细心就能写好的,不难。但是难点在于0/1互换以及询问最长连续1的长度。
之前的Help with Intervals的区间翻转直接移植过来是会失败的。因为那道题并没有数据域。因此在进行翻转操作的时候没有更新数据域,仅仅只是翻转了标记。
但是这道题,一旦进行了翻转操作,就必须更新当前结点的数据域,否则对翻转来说,Down操作根本没用!
除此以外,对于最值的查询,也不像Hotel那样,那时候因为查询的是特定长度,自左向右,所以只要不是单纯在左儿子或者右儿子当中,就可以直接地返回查询值了。
但是这次,就要三次:
如果单纯左儿子,递归进入左儿子,并返回值。
如果单纯右儿子,递归进入右儿子,并返回值。
否则的话,再次递归进入左儿子,递归进入右儿子,之后再计算如果在合并之后的区间内的话,既在查询区间内又在合并后的连续区间内的值,以上三个值取最值并返回。
代码如下:
/*
ID: j.sure.1
PROG:
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;
/****************************************/
const int N = 1e5+5;
int n, m, u, x[N];
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define MID int m = (l+r)>>1
#define node int l, int r, int rt
#define quote Node &ll = t[rt<<1], &rr = t[rt<<1|1], &tt = t[rt];
#define Swap(a, b) u = a; a = b; b = u;
struct Node {
int maxi[2], left[2], right[2];
int sum;
int cov; bool opp;
}t[N<<2];
void EX(int rt, int len)
{
Swap(t[rt].maxi[0], t[rt].maxi[1]);
Swap(t[rt].left[0], t[rt].left[1]);
Swap(t[rt].right[0], t[rt].right[1]);
t[rt].sum = len - t[rt].sum;//0、1个数互换
}
void toop(int rt, int len)
{
if(t[rt].cov != -1) {
t[rt].cov = !t[rt].cov;
t[rt].sum = len * (t[rt].cov == 1);
for(int i = 0; i < 2; i++) {
t[rt].left[i] = t[rt].right[i] = t[rt].maxi[i] = (i == t[rt].cov)*len;
}
}
else {
t[rt].opp = !t[rt].opp;
EX(rt, len);
}
}//难点,这里不只是翻转了标记,还需要更新数据域
void Down(int rt, int len)
{
quote;
if(tt.cov != -1) {
ll.cov = rr.cov = tt.cov;//下放标记
ll.opp = rr.opp = 0;//翻转无效
ll.sum = (tt.cov == 1) * (len+1>>1);
rr.sum = (tt.cov == 1) * (len>>1);//根据标记更新左右儿子的和
for(int i = 0; i < 2; i++) {
ll.left[i] = ll.right[i] = ll.maxi[i] = (tt.cov == i)*(len+1>>1);
rr.left[i] = rr.right[i] = rr.maxi[i] = (tt.cov == i)*(len >> 1);
}//更新最值!
tt.cov = -1;
}
if(tt.opp) {//左右儿子根据翻转标记进行更新
toop(rt<<1, len+1>>1);
toop(rt<<1|1, len>>1);
tt.opp = 0;
}
}
void Up(int rt, int len)
{
quote;
tt.sum = ll.sum + rr.sum;
for(int i = 0; i < 2; i++) {
tt.left[i] = ll.left[i];
tt.right[i] = rr.right[i];
if(tt.left[i] == len+1>>1) tt.left[i] += rr.left[i];
if(tt.right[i] == len>>1) tt.right[i] += ll.right[i];
tt.maxi[i] = max(ll.right[i] + rr.left[i], max(ll.maxi[i], rr.maxi[i]));
}
}
void update(int c, int L, int R, node)
{
quote;
if(L <= l && r <= R) {
if(c < 2) {
tt.cov = c; tt.opp = 0;
tt.sum = c ? r-l+1 : 0;
for(int i = 0; i < 2; i++) {
tt.left[i] = tt.right[i] = tt.maxi[i] = (c == i)*(r-l+1);
}
}
else toop(rt, r-l+1);
return ;
}
Down(rt, r-l+1);
MID;
if(L <= m) update(c, L, R, lson);
if(m < R) update(c, L, R, rson);
Up(rt, r-l+1);
}
int Qmax(int L, int R, node)
{
quote;
if(L <= l && r <= R) {
return tt.maxi[1];
}
Down(rt, r-l+1);
MID;
int retl, retr;
if(R <= m) return Qmax(L, R, lson);//单纯在左儿子当中
if(m < L) return Qmax(L, R, rson);//单纯在右儿子当中
retl = Qmax(L, R, lson);
retr = Qmax(L, R, rson);
int ret = max(retl, retr);
int ans = min(m-L+1, ll.right[1]) + min(R-m, rr.left[1]);//在合并之后的区间内
return max(ret, ans);//三段区间取最值
}//难点
int Qsum(int L, int R, node)
{
quote;
if(L <= l && r <= R) {
return tt.sum;
}
Down(rt, r-l+1);
MID;
int ret = 0;
if(L <= m) ret += Qsum(L, R, lson);
if(m < R) ret += Qsum(L, R, rson);
return ret;
}
void build(node)
{
t[rt].cov = -1; t[rt].opp = 0;
if(l == r) {
scanf("%d", &t[rt].sum);
t[rt].cov = t[rt].sum;
for(int i = 0; i < 2; i++)
t[rt].maxi[i] = t[rt].left[i] = t[rt].right[i] = (t[rt].cov == i);
return ;
}
MID;
build(lson); build(rson);
Up(rt, r-l+1);
}
int main()
{
#ifdef J_Sure
freopen("000.in", "r", stdin);
freopen("999.out", "w", stdout);
#endif
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
build(0, n-1, 1);
while(m--) {
int op, A, B;
scanf("%d%d%d", &op, &A, &B);
if(op < 3) {
update(op, A, B, 0, n-1, 1);
}
else {
if(op == 3)
printf("%d\n", Qsum(A, B, 0, n-1, 1));
else
printf("%d\n", Qmax(A, B, 0, n-1, 1));
}
}
}
return 0;
}