HDU 3397 Sequence operation(线段树:成段更新,查询连续目标子串长度)

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: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>


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值