HDU 3397 Sequence operation(线段树:成段更新,查询连续目标子串长度)
http://acm.hdu.edu.cn/showproblem.php?pid=3397
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]
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:http://blog.csdn.net/u013480600/article/details/22284341也是同时有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了:http://blog.csdn.net/u013480600/article/details/22421981.可以看成是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>