P2572 [SCOI2010]序列操作 (线段树)
【题目描述】
lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:
0 a b 把[a, b]区间内的所有数全变成0
1 a b 把[a, b]区间内的所有数全变成1
2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0
3 a b 询问[a, b]区间内总共有多少个1
4 a b 询问[a, b]区间内最多有多少个连续的1
对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?
【输入格式】
输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目
第二行包括n个数,表示序列的初始状态
接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b<n)表示对于区间[a, b]执行标号为op的操作
1<=n,m<=100000
【输出格式】
对于每一个询问操作,输出一行,包括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
5 2 6 5
【思路】
其实就像学长说的线段树这种题应该都是签到题,只需要在纸面上明确几个标记的作用,合并,下传(但是我明确不了啊),就可以对线段树做到即插即用。
对操作进行分类,可以发现用两个标记就可以表现三种操作
tag:表示是否对这个区间进行赋值,-1表示没有赋值,0表示赋0,1表示赋1
rev:表示是否对这个区间进行反转,0表示不反转,1表示要反转
再看询问,询问区间内1的个数和连续长度
两个区间合并影响长度的是两边,所以我们要把左中右分别保存,因为0,1的反转会影响到这些值,所以我们还需要同时保存0,1
之所以要这样保存,是因为区间的反转并不能像数量一样直接通过对区间长度进行操作获得,所以需要这样操作
(区间长度的求取也要注意,比如我第一次样例没过就是因为长度比较的长度用的是一个询问值)
(我觉得我需要换掉之前的线段树板子,另找个大佬的)
询问的处理,询问1的个数比较简单,就和之前求和一样
区间长度的处理需要自己想一下
(照着题解打查了半天)
如果找左边,然后只需要左边,右边不需要呢,那就只返回左边
右边同理
如果都有再进行合并
【总结】
主要还是从本质和运行时产生的抽象模型彻底理解一段代码的意义,明确一段代码究竟要干什么,又是怎么实现的
不能盲目的照搬题解,只图AC,那样子一点意义也没有
【代码(我觉得我要重构一下(改天吧))】
#include"pch.h" #include<cstdio> #include<cstring> #include<algorithm> #define ls p<<1 #define rs p<<1|1 #define midd ((l+r)>>1) using namespace std; const int MAXN = 100010; int arr[MAXN]; struct Tree { int sum; int maxn[2]; int lmax[2], rmax[2]; int tag; bool rev; int l, r; Tree(int d=0,int e=0,int a = 0, int b = -1, bool c = 0) { memset(maxn, 0, sizeof(maxn)); memset(lmax, 0, sizeof(lmax)); memset(rmax, 0, sizeof(rmax)); sum = a; tag = b; rev = c; l = d; r = e; } }tree[(MAXN << 3)+1000]; int op, nl, nr; void push_up(int p, int l, int r) { int ll = l, lr = midd, rl = midd + 1, rr = r; tree[p].sum = tree[ls].sum + tree[rs].sum; for (int i = 0; i <= 1; i++) { tree[p].lmax[i] = tree[ls].lmax[i]; switch (i) { case 0: { if (tree[ls].sum == 0) tree[p].lmax[i] += tree[rs].lmax[i]; break; } case 1: { if (tree[ls].sum == lr+1-ll) tree[p].lmax[i] += tree[rs].lmax[i]; break; } default: break; } tree[p].rmax[i] = tree[rs].rmax[i]; switch (i) { case 0: { if (tree[rs].sum == 0) tree[p].rmax[i] += tree[ls].rmax[i]; break; }case 1: { if (tree[rs].sum == rr+1-rl) tree[p].rmax[i] += tree[ls].rmax[i]; } break; default: break; } tree[p].maxn[i] = tree[ls].rmax[i] + tree[rs].lmax[i]; tree[p].maxn[i] = max(tree[p].maxn[i], tree[ls].maxn[i]); tree[p].maxn[i] = max(tree[p].maxn[i], tree[rs].maxn[i]); } return; } void push_down(int p, int l, int r) { int ll = l, lr = midd, rl = midd + 1, rr = r; if (tree[p].tag != -1) { int t = tree[p].tag; tree[p].rev = 0; tree[ls].rev = tree[rs].rev = 0; tree[ls].sum = (lr+1 - ll)*t; tree[rs].sum = (rr+1 - rl)*t; tree[ls].tag = tree[rs].tag = t; int ll = l, lr = midd, rl = midd + 1, rr = r; tree[ls].maxn[t] = tree[ls].rmax[t] = tree[ls].lmax[t] = lr+1 - ll; tree[rs].maxn[t] = tree[rs].rmax[t] = tree[rs].lmax[t] = rr+1 - rl; t = !t; tree[ls].maxn[t] = tree[ls].rmax[t] = tree[ls].lmax[t] = 0; tree[rs].maxn[t] = tree[rs].rmax[t] = tree[rs].lmax[t] = 0; tree[p].tag = -1; } if (tree[p].rev != 0) { Tree tmp = tree[ls]; tree[ls].sum = lr + 1 - ll - tree[ls].sum; tree[rs].sum = rr + 1 - rl - tree[rs].sum; for (int i = 0; i <= 1; i++) { tree[ls].maxn[i] = tmp.maxn[!i]; tree[ls].rmax[i] = tmp.rmax[!i]; tree[ls].lmax[i] = tmp.lmax[!i]; } tmp = tree[rs]; for (int i = 0; i <= 1; i++) { tree[rs].maxn[i] = tmp.maxn[!i]; tree[rs].rmax[i] = tmp.rmax[!i]; tree[rs].lmax[i] = tmp.lmax[!i]; } if (tree[ls].tag != -1) tree[ls].tag = !tree[ls].tag; else tree[ls].rev = !tree[ls].rev; if (tree[rs].tag != -1) tree[rs].tag = !tree[rs].tag; else tree[rs].rev = !tree[rs].rev; tree[p].rev = 0; } } void Build(int p, int l, int r) { tree[p].tag = -1; if (l == r) { tree[p].sum = arr[l]; for (int i = 0; i <= 1; i++) tree[p].maxn[i] = tree[p].lmax[i] = tree[p].rmax[i] = (arr[l] == i); return; } int mid = midd; Build(ls, l, mid); Build(rs, mid + 1, r); push_up(p, l, r); return; } void update(int p, int l, int r) { push_down(p, l, r); if (nl <= l && r <= nr) { switch (op) { case 0: case 1: { tree[p].sum = (r + 1 - l)*op; tree[p].tag = op; tree[p].maxn[op] = tree[p].lmax[op]=tree[p].rmax[op] = r + 1 - l; op = !op; tree[p].maxn[op] = tree[p].lmax[op]=tree[p].rmax[op] = 0; op = !op; break; } case 2: { Tree tmp = tree[p]; tree[p].sum = r + 1 - l - tree[p].sum; for (int i = 0; i <= 1; i++) { tree[p].maxn[i] = tmp.maxn[!i]; tree[p].lmax[i] = tmp.lmax[!i]; tree[p].rmax[i] = tmp.rmax[!i]; } tree[p].rev = !tree[p].rev; break; } } return; } int mid = midd; if (nl <= mid) update(ls, l, mid); if (nr > mid) update(rs, mid + 1, r); push_down(ls, l, mid); push_down(rs, mid + 1, r); push_up(p, l, r); return; } int query_sum(int p, int l, int r) { push_down(p, l, r); if (nl <= l && r <= nr) { return tree[p].sum; } int mid = midd; int ans = 0; if (nl <= mid) ans += query_sum(ls, l, mid); if (nr > mid) ans += query_sum(rs, mid + 1, r); return ans; } Tree query_suc(int p, int l, int r) { push_down(p, l, r); if (nl <= l && r <= nr) { Tree t = tree[p]; t.l = l; t.r = r; return t; } Tree ret, L , R; int mid = midd; int fl = 0, fr = 0; if (nl <= mid) { L = query_suc(ls, l, mid); fl = 1; } if (nr > mid) { R = query_suc(rs, mid + 1, r); fr = 1; } if (fr == 0 && fl == 1) return L; if (fl == 0 && fr == 1) return R; ret.sum = L.sum + R.sum; int ll = l, lr = midd, rl = midd + 1, rr = r; for (int i = 0; i <= 1; i++) { ret.lmax[i] = L.lmax[i]; switch (i) { case 0: { if (L.sum == 0) ret.lmax[i] += R.lmax[i]; break; } case 1: { if (L.sum == L.r+1-L.l) ret.lmax[i] += R.lmax[i]; break; } default: { break; } } ret.rmax[i] =R.rmax[i]; switch (i) { case 0: { if (R.sum == 0) ret.rmax[i] += L.rmax[i]; break; } case 1: { if (R.sum == R.r+1-R.l) ret.rmax[i] += L.rmax[i]; break; } default: { break; } } ret.maxn[i] = L.rmax[i] + R.lmax[i]; ret.maxn[i] = max(ret.maxn[i], max(L.maxn[i], R.maxn[i])); } ret.l = L.l, ret.r = R.r; return ret; } void debug(int p, int l, int r) { int mid = midd; printf("p=%d l=%d r=%d\n", p, l, r); for (int i = 0; i <= 1; i++) { printf("i=%d :: maxn=%d lmaxn=%d rmax=%d sum =%d\n", i, tree[p].maxn[i], tree[p].lmax[i], tree[p].rmax[i], tree[p].sum); printf(" tag=%d rev=%d\n\n", tree[p].tag, tree[p].rev); } if (l == r) return; debug(ls, l, mid); debug(rs, mid + 1, r); return; } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &arr[i]); } Build(1, 1, n); for (int i = 1; i <= m; i++) { scanf("%d%d%d", &op, &nl, &nr); nl++, nr++; //printf("\n\n\n"); //debug(1, 1, n); switch (op) { case 0: update(1, 1, n); break; case 1: update(1, 1, n); break; case 2: update(1, 1, n); break; case 3: { printf("%d\n", query_sum(1, 1, n)); break; } case 4: { Tree t = query_suc(1, 1, n); printf("%d\n", t.maxn[1]); break; } default:break; } } return 0; }