codeforces 145E - Lucky Queries

    题目大意:给一个长度为n的47序列(就是只有4和7),两种操作:

操作1:count 要求输出当前数列的最长非下降子序列(注意是子序列,不是子串。。。一开始没看清题WA半天)。

操作2 : switch l r 将区间[l,r]之间的数反转(这里的反转指的是4变成7 7变成4)

然后就是给你一堆操作,对每一个操作1输出相应的值。。。

PS:这竟然是div1 的 E,我觉得就是一道很中规中矩的线段树啊。。。

    思路:在线段树中维护一下值:

len4表示全是4的最长子序列。

len7表示全是7的最长子序列。

len47表示以4开头以7结尾的最长子序列。

rev当然是懒惰标记啦。。。

对于操作2,因为反转之后对len4,len7,len47的值影响较大(主要还是len47),所以我一般对这样的题都是直接维护两个值,一个是当前的,一个是反转后的,当进行反转操作时再交换就好了。

对于两个区间合并 len4和len7很好更新,这里就不讲了,主要还是len47(其实也很简单的。。。),len47就三种情况,1,左边的len4+右边的len47。2,左边的len4+右边的len7。3左边的len47+右边的len7。三种情况取个大的就行了。还有。。。没什么要注意了吧。。两个操作都是很普通的线段树操作,没什么可讲的了,代码实现如下。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 1000100
#define mid ((t[p].l+t[p].r)>>1)
#define ls (p<<1)
#define rs (ls|1)
using namespace std;
struct tree
{
    int l,r;
    int len4[2],len7[2],len47[2];
    int rev;
}t[maxn<<2];
int a[maxn];
int max(int a,int b)
{
    return a>b?a:b;
}
void pushup(int p,int c)
{
    t[p].len4[c]=t[ls].len4[c]+t[rs].len4[c];
    t[p].len7[c]=t[ls].len7[c]+t[rs].len7[c];
    int tmp=t[ls].len4[c]+max(t[rs].len7[c],t[rs].len47[c]);
    t[p].len47[c]=max(tmp,t[ls].len47[c]+t[rs].len7[c]);
}
void change(int p)
{
    t[p].rev^=1;
    swap(t[p].len4[0],t[p].len4[1]);
    swap(t[p].len7[0],t[p].len7[1]);
    swap(t[p].len47[0],t[p].len47[1]);
}
void pushdown(int p)
{
    if(t[p].rev)
    {
        change(ls);
        change(rs);
        t[p].rev=0;
    }
}
void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r,t[p].rev=0;
    if(l==r)
    {
        if(a[l])
        {
            t[p].len7[0]=t[p].len4[1]=1;
            t[p].len7[1]=t[p].len4[0]=0;
        }
        else
        {
            t[p].len7[0]=t[p].len4[1]=0;
            t[p].len7[1]=t[p].len4[0]=1;
        }
        t[p].len47[0]=t[p].len47[1]=0;
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(p,0);
    pushup(p,1);
}
int get(int p,int x)
{
    if(t[p].l==t[p].r)
    {
        if(t[p].len4[0])
        return 0;
        return 1;
    }
    pushdown(p);
    if(x>mid)
    return get(rs,x);
    return get(ls,x);
}
void reverse(int p,int l,int r)
{
    if(t[p].l==l&&t[p].r==r)
    {
        change(p);
        return;
    }
    pushdown(p);
    if(l>mid)
    reverse(rs,l,r);
    else if(r<=mid)
    reverse(ls,l,r);
    else
    {
        reverse(ls,l,mid);
        reverse(rs,mid+1,r);
    }
    pushup(p,0);
    pushup(p,1);
}
char str[maxn],tmp[10];
int main()
{
    int n,m,i;
    scanf("%d%d",&n,&m);
    scanf("%s",str+1);
    for(i=1;i<=n;i++)
    {
        if(str[i]=='4')
        a[i]=0;
        else
        a[i]=1;
    }
    build(1,1,n);
    while(m--)
    {
        int l,r;
        scanf("%s",tmp);
        if(tmp[0]=='s')
        {
            scanf("%d%d",&l,&r);
            reverse(1,l,r);
        }
        else
        {
            printf("%d\n",max(t[1].len4[0],max(t[1].len7[0],t[1].len47[0])));
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值