【线段树】COCI2010-2011(CONTEST#6)[STEP]题解

题目概述

有一个长度为n的序列,刚开始全是L,给出te个操作,每次操作给出一个x,表示把x从L改为R(R改为L)。每次操作后询问最长的字串,这个字串需要满足相邻位置符号不同。

解题报告

刚开始我是这么想的:把序列分块成一片片满足的区域,比如LRRLRRRLRLRL被分为|LR|RLR|R|RLRLRL|。然后记录father[i]和r[i]表示i所在区域最左边的编号和i能够向右推的个数,用线段树维护这些区域(加加减减,改来改去),这种做法常数显然很大,还容易写错,可想而知我华丽丽的炸了(WA+TLE)。

后来,Lynstery飘过来说:“这种垃圾题”,然后告诉了我强的不行的做法:序列相邻两个之间如果是相同的,就记为0,否则记为1,举个例子:

L R R L R R R L R L R L
 1 0 1 1 0 0 1 1 1 1 1

那么求最长子串就转化为求最长连续1的个数。
接下来还是用线段树处理,每个节点记录MAX,pre,suf表示最长子串,最长前缀和最长后缀,由于每次只是单点修改,所以维护这三个信息是比较简单的:

pre=左儿子pre
if (左儿子MAX=左儿子区间跨度) pre=左儿子MAX+右儿子pre
//左儿子MAX=左儿子区间跨度其实等价于左儿子能够全选
suf=右儿子suf
if (右儿子MAX=右儿子区间跨度) suf=右儿子MAX+左儿子suf
MAX=max(左儿子MAX,右儿子MAX,pre,suf,左儿子suf+右儿子pre)

那么最大值就是MAX[1]。

示例程序

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200000,maxt=4*maxn,MAXINT=((1<<30)-1)*2+1;

int n,te;
bool vis[maxn+5];
struct SegmentTree
{
    int l[maxt+5],r[maxt+5],MAX[maxt+5],pre[maxt+5],suf[maxt+5];
    void Build(int L,int R,int p=1)
    {
        l[p]=L;r[p]=R;MAX[p]=pre[p]=suf[p]=0;
        if (L==R) return;int mid=L+(R-L>>1);
        Build(L,mid,p<<1);Build(mid+1,R,p<<1|1);
    }
    void Pushup(int p)
    {
        pre[p]=pre[p<<1];
        if (MAX[p<<1]==r[p<<1]-l[p<<1]+1) pre[p]=MAX[p<<1]+pre[p<<1|1];
        suf[p]=suf[p<<1|1];
        if (MAX[p<<1|1]==r[p<<1|1]-l[p<<1|1]+1) suf[p]=suf[p<<1]+MAX[p<<1|1];
        MAX[p]=max(max(pre[p],suf[p]),max(MAX[p<<1],MAX[p<<1|1]));
        MAX[p]=max(MAX[p],suf[p<<1]+pre[p<<1|1]);
    }
    void Update(int pos,int k,int p=1)
    {
        if (pos<l[p]||r[p]<pos) return;
        if (l[p]==r[p]) {MAX[p]=pre[p]=suf[p]=k;return;}
        Update(pos,k,p<<1);Update(pos,k,p<<1|1);
        Pushup(p);
    }
};
SegmentTree tr;

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
char readc()
{
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; else return *l++;
}
int readi(int &x)
{
    int tot=0,f=1;char ch=readc(),lst='+';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=readc();
    x=tot*f;
    return Eoln(ch);
}
int main()
{
    freopen("step.in","r",stdin);
    freopen("step.out","w",stdout);
    readi(n);readi(te);tr.Build(1,n-1);
    while (te--)
    {
        int x;readi(x);
        if (x>1) tr.Update(x-1,vis[x-1]^=1);
        if (x<n) tr.Update(x,vis[x]^=1);
        printf("%d\n",tr.MAX[1]+1);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值