HDU 3397 Sequence operation(线段树:成段更新,查询连续目标子串长度)
2014-03-28 22:29
447 查看
HDU 3397 Sequence operation(线段树:成段更新,查询连续目标子串长度)
http://acm.hdu.edu.cn/showproblem.php?pid=3397
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]
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.
分析:
注意在总的query地方可以不用编写查询前缀和后缀的函数,特别注意优化二的地方.
1.本题有点类似于POJ3225:/article/1331285.html也是同时有cover操作和异或操作,但是这里用异或标记位的话,当某个节点被异或了,无法在不向下递归的情况下计算出sub,pre和suf的信息.所以就不设异或位了,如果有异或操作直接递归到底并PushUp更新.
2.维护一棵线段树,根本的信息是cover(为1或0表示被覆盖成了1或0,如果是-1表示子节点中的cover值不一致),辅助查询的信息是sum(节点中1的个数), sub(节点所指的区间内最长连续1的个数), pre(节点所指区间最长前缀连续1的个数), suf(节点所指的区间最长后缀连续1的个数). 本线段树有2个update操作,4个query操作. 这里的话又类似于HDU3308了:/article/1331280.html.可以看成是HDU3308的稍微加强版.
3.线段树的操作:
PushUp(i,l,r): 根据儿子节点的cover 和sub,sum suf,pre 更新父节点的所有信息.考虑周到即可.
PushDown(i,l,r):
根据父节点的cover ,sub,suf,sum,pre,更新子节点的所有对应信息.
build(i,l,r): 递归建树,并PushUp更新父节点信息.
update_1(ql,qr,v,i,l,r):表示将[ql,qr]与[l,r]区间重合的部分置v值.
如果ql<=l 且r<=qr 直接更新cover,sub,sum,suf,pre的信息.否则的话先PushDown,然后递归update_1左右儿子,然后在PushUp.
update_2(ql,qr,i,l,r):表示将[ql,qr]与[l,r]区间重合的部分执行异或操作.如果l==r 那么直接对叶节点异或,更新所有信息.否则先PushDown然后分段update_2,然后PushUp.
query_sum(ql,qr,i,l,r):表示查询[ql,qr]与[l,r]重合部分的1的个数.如果ql<=l 且r<=qr 直接返回sum[i],否则,PushDown,分段查询. 返回总和.
query_pre(ql,qr,i,l,r):查询[ql,qr]与[l,r]重合部分的最长前缀连续1的个数.如果ql<=l && r<=qr 直接返回 pre[i],否则先PushDown,然后如果公共部分仅在[l,r]的左儿子,那就递归返回左儿子的.如果仅在右边,那就递归返回右儿子的.如果两边都有公共部分,那么就先算左儿子的,如果在考虑需不需要把右儿子的前缀公共部分也加上去.
query_suf(ql,qr,i,l,r): 查询[ql,qr]与[l,r]重合部分的最长后缀连续1的个数.如果ql<=l && r<=qr 直接返回 suf[i], 否则先PushDown,然后如果公共部分仅在[l,r]的左儿子,那就递归返回左儿子的.如果仅在右边,那就递归返回右儿子的.如果两边都有公共部分,那么就先算右儿子的,如果在考虑需不需要把左儿子的后缀公共部分也加上去.
query(ql,qr,i,l,r):查询[ql,qr]与[l,r]重合部分的最长连续1序列的长度.
如果ql<=l && r<=qr 那么直接返回sub[i]. 否则先PushDown,然后要对比左边儿子的sub(如果有公共部分)和右边儿子的sub(如果有公共部分) ,并且还要对比左儿子的后缀加上右儿子的前缀(即[ql,qr]yu[l,r]的左右儿子都有公共部分)
.最终返回结果.
先提交的一份代码超时了,update_2的更新操作优化之后就AC了
AC代码:1062ms.
http://acm.hdu.edu.cn/showproblem.php?pid=3397
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]
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.
分析:
注意在总的query地方可以不用编写查询前缀和后缀的函数,特别注意优化二的地方.
1.本题有点类似于POJ3225:/article/1331285.html也是同时有cover操作和异或操作,但是这里用异或标记位的话,当某个节点被异或了,无法在不向下递归的情况下计算出sub,pre和suf的信息.所以就不设异或位了,如果有异或操作直接递归到底并PushUp更新.
2.维护一棵线段树,根本的信息是cover(为1或0表示被覆盖成了1或0,如果是-1表示子节点中的cover值不一致),辅助查询的信息是sum(节点中1的个数), sub(节点所指的区间内最长连续1的个数), pre(节点所指区间最长前缀连续1的个数), suf(节点所指的区间最长后缀连续1的个数). 本线段树有2个update操作,4个query操作. 这里的话又类似于HDU3308了:/article/1331280.html.可以看成是HDU3308的稍微加强版.
3.线段树的操作:
PushUp(i,l,r): 根据儿子节点的cover 和sub,sum suf,pre 更新父节点的所有信息.考虑周到即可.
PushDown(i,l,r):
根据父节点的cover ,sub,suf,sum,pre,更新子节点的所有对应信息.
build(i,l,r): 递归建树,并PushUp更新父节点信息.
update_1(ql,qr,v,i,l,r):表示将[ql,qr]与[l,r]区间重合的部分置v值.
如果ql<=l 且r<=qr 直接更新cover,sub,sum,suf,pre的信息.否则的话先PushDown,然后递归update_1左右儿子,然后在PushUp.
update_2(ql,qr,i,l,r):表示将[ql,qr]与[l,r]区间重合的部分执行异或操作.如果l==r 那么直接对叶节点异或,更新所有信息.否则先PushDown然后分段update_2,然后PushUp.
query_sum(ql,qr,i,l,r):表示查询[ql,qr]与[l,r]重合部分的1的个数.如果ql<=l 且r<=qr 直接返回sum[i],否则,PushDown,分段查询. 返回总和.
query_pre(ql,qr,i,l,r):查询[ql,qr]与[l,r]重合部分的最长前缀连续1的个数.如果ql<=l && r<=qr 直接返回 pre[i],否则先PushDown,然后如果公共部分仅在[l,r]的左儿子,那就递归返回左儿子的.如果仅在右边,那就递归返回右儿子的.如果两边都有公共部分,那么就先算左儿子的,如果在考虑需不需要把右儿子的前缀公共部分也加上去.
query_suf(ql,qr,i,l,r): 查询[ql,qr]与[l,r]重合部分的最长后缀连续1的个数.如果ql<=l && r<=qr 直接返回 suf[i], 否则先PushDown,然后如果公共部分仅在[l,r]的左儿子,那就递归返回左儿子的.如果仅在右边,那就递归返回右儿子的.如果两边都有公共部分,那么就先算右儿子的,如果在考虑需不需要把左儿子的后缀公共部分也加上去.
query(ql,qr,i,l,r):查询[ql,qr]与[l,r]重合部分的最长连续1序列的长度.
如果ql<=l && r<=qr 那么直接返回sub[i]. 否则先PushDown,然后要对比左边儿子的sub(如果有公共部分)和右边儿子的sub(如果有公共部分) ,并且还要对比左儿子的后缀加上右儿子的前缀(即[ql,qr]yu[l,r]的左右儿子都有公共部分)
.最终返回结果.
先提交的一份代码超时了,update_2的更新操作优化之后就AC了
AC代码:1062ms.
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define lson i*2,l,m #define rson i*2+1,m+1,r #define root 1,0,n-1 const int MAXN = 100000+100; int cover[MAXN*4];//根本信息 int sum[MAXN*4],sub[MAXN*4],pre[MAXN*4],suf[MAXN*4];//查询信息 void PushDown(int i,int l,int r) { int m=(l+r)/2; if(cover[i]!=-1) { cover[i*2]=cover[i*2+1]=cover[i]; if(cover[i]==0) { sum[i*2]=sum[i*2+1]=0; sub[i*2]=sub[i*2+1]=0; pre[i*2]=pre[i*2+1]=0; suf[i*2]=suf[i*2+1]=0; } else if(cover[i]==1) { sum[i*2]=sub[i*2]=pre[i*2]=suf[i*2]=m-l+1; sum[i*2+1]=sub[i*2+1]=pre[i*2+1]=suf[i*2+1]=r-m; } } } void PushUp(int i,int l,int r) { int m=(l+r)/2; //cover if(cover[i*2]==-1 || cover[i*2+1]==-1) cover[i]=-1; else if(cover[i*2] != cover[i*2+1]) cover[i]=-1; else cover[i]=cover[i*2]; //sum sum[i]=sum[i*2]+sum[i*2+1]; //sub sub[i]=max(suf[i*2]+pre[i*2+1] , max(sub[i*2] , sub[i*2+1])); //pre pre[i]=pre[i*2]; if(pre[i] == m-l+1) pre[i] += pre[i*2+1]; //suf suf[i]=suf[i*2+1]; if(suf[i]==r-m) suf[i] += suf[i*2]; } void build(int i,int l,int r) { if(l==r) { scanf("%d",&sum[i]); sub[i]=suf[i]=pre[i]=cover[i]=sum[i]; return ; } int m=(l+r)/2; build(lson); build(rson); PushUp(i,l,r); } void update_1(int ql,int qr,int v,int i,int l,int r) { if(ql<=l && r<=qr) { cover[i]=v; sum[i]=(r-l+1)*v; sub[i]=pre[i]=suf[i]= (v?r-l+1:0); return ; } PushDown(i,l,r); int m=(l+r)/2; if(ql<=m) update_1(ql,qr,v,lson); if(m<qr) update_1(ql,qr,v,rson); PushUp(i,l,r); } void update_2(int ql,int qr,int i,int l,int r) { if(ql<=l && r<=qr)//优化一,此处不优化就超时 { if(cover[i]!=-1) { cover[i] ^=1; sum[i]=sub[i]=pre[i]=suf[i]= (cover[i]?(r-l+1):0) ; return ; } } PushDown(i,l,r); int m=(l+r)/2; if(ql<=m) update_2(ql,qr,lson); if(m<qr) update_2(ql,qr,rson); PushUp(i,l,r); } int query_sum(int ql,int qr,int i,int l,int r) { if(ql<=l && r<=qr) return sum[i]; PushDown(i,l,r); int m=(l+r)/2; int res=0; if(ql<=m) res += query_sum(ql,qr,lson); if(m<qr) res += query_sum(ql,qr,rson); return res; } /* int query_pre(int ql,int qr,int i,int l,int r) { if(ql<=l && r<=qr) return pre[i]; PushDown(i,l,r); int m=(l+r)/2; if(qr<=m) return query_pre(ql,qr,lson); if(m<ql) return query_pre(ql,qr,rson); int L = query_pre(ql,qr,lson); if(L==m-l+1) L += query_pre(ql,qr,rson); return L; } int query_suf(int ql,int qr,int i,int l,int r) { if(ql<=l && r<=qr) return suf[i]; PushDown(i,l,r); int m=(r+l)/2; if(qr<=m) return query_suf(ql,qr,lson); if(m<ql) return query_suf(ql,qr,rson); int R = query_suf(ql,qr,rson); if(R == r-m) R += query_suf(ql,qr,lson); return R; } */ int query(int ql,int qr,int i,int l,int r) { if(ql<=l && r<=qr) return sub[i]; PushDown(i,l,r); int m=(l+r)/2; int len1=0,len2=0,len3=0; if(ql<=m) len1 = query(ql,qr,lson); if(m<qr) len2=query(ql,qr,rson); if(ql<=m && m<qr) len3 = min(m-ql+1,suf[i*2])+min(qr-m,pre[i*2+1]);//优化二 //len3 = query_suf(ql,qr,lson)+query_pre(ql,qr,rson); //printf("l==%d,r==%d,len1=%d len2=%d len3=%d suf[i*2]=%d,pre[i*2+1]=%d\n",l,r,len1,len2,len3,suf[i*2],pre[i*2+1]); return max(len3,max(len1,len2)); } int main() { int T,n,m; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); build(root); while(m--) { int op,a,b; scanf("%d%d%d",&op,&a,&b); if(op==0) update_1(a,b,0,root); else if(op==1) update_1(a,b,1,root); else if(op==2) update_2(a,b,root); else if(op==3) printf("%d\n",query_sum(a,b,root)); else if(op==4) printf("%d\n",query(a,b,root)); } } return 0; } </span>
相关文章推荐
- HDU 3308 LCIS(线段树:单点更新,求最大连续子串)
- HDU 3308 线段树 最长连续上升子序列 单点更新 区间查询
- HDU1540 Tunnel Warfare(线段树:维护最大连续子串)
- hdu 3577 线段树,成段更新 好题 查询区间的最大覆盖次数
- hdu 1540 线段树 点所在的区间最大连续长度
- M - Sequence operation HDU - 3397 线段树,成段更新,区间合并)
- hdu 3397 Sequence operation 线段树 区间和 连续1的个数
- HDU 3397 线段树区间染色 区间查询
- HDU 3397 Sequence operation (线段树,成段更新,区间合并)
- hdu 3397 Sequence Operation 线段树维护区间前后缀和,求子区间连续最值
- HDU 1556 给连续个球涂色-线段树-(区间更新,单点查询)
- HDU 3308 LCIS(线段树+区间合并+最长递增连续子串)
- hdu 1540 Tunnel Warfare 线段树 单点更新,查询区间长度,区间合并
- POJ 题目2892 Tunnel Warfare(线段树单点更新查询,求单点所在最大连续区间长度)
- hdu 1540 Tunnel Warfare 线段树 单点更新,查询区间长度,区间合并
- HDU 3308 线段树 最长连续上升子序列 单点更新 区间查询
- HDU 4027 ( 线段树 -- 成段更新)
- 求字符串中最长连续数字子串的长度
- 线段树(成段更新) HDU 1698 Just a Hook
- HDU 5592——ZYB's Premutation——————【线段树单点更新、单点查询】