upc 2555: Longest Non-decreasing Substring 区间合并线段树

本文探讨了一个利用区间合并线段树解决特定字符串操作问题的方法,详细解释了如何通过线段树来实现翻转操作并计算最长非递减子串长度,包括旋转操作的实现与细节处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

You are given a string S no longer than 100,000 consisting of characters 0-9 only. And two kinds of operations will be performed on S:
 Flip i j: for each i <= k <= j, Sk is replaced substituted with 9 - Sk.
 Query: you are to calculate the length of the longest non-decreasing substring of S.

Input

The first line of the input contains a single integer t, the number of test cases.
For each test case, the first line contains two integers, n and m(1 <= m <= 5000), indicating the length of S and the number of operations to perform.
Then m lines follow. Each line gives a single operation that is either “flip i j”(1 <= i <= j <= n) or “query”.
There is a blank line after each test case.

Output

For each query operation, print the length of the longest non-decreasing substring of S on a single line.
Print a blank line after each test case.

Sample Input

110 60123456789queryflip 1 10queryflip 1 10flip 3 4query

Sample Output

1016

  题意:flip操作是把区间内每个数用9减,询问操作是询问目前最长的不减序列有多长。

  这道题折腾了我几天。。。一直WA,还想不明白为什么WA。。折腾到今天终于过了,看来是我线段树太水了,很多细节没理解到位。。这道题挺好的。

  区间合并线段树一般需要维护很多东西,从左开始,从右开始,整个区间。。

  这道题要维护从左开始不减最长,从右开始不减最长,整个区间不减最长,左边第一个数字,右边第一个数字。。又因为有旋转操作,旋转的话不减就变成了不增。所以还要维护从左开始不增最长,从右开始不增最长,整个区间不增最长。。

  用一个rot数组代表该区间旋转了几次。在更新的过程中如果该节点旋转次数是奇数次,就pushdown。也就是把一整个区间分成几个不重合区间的思想,这样就不用查询到底。

  一开始我把旋转函数写在了maintain函数里面,每次maintain的时候判断这个区间的rot,如果是奇数就旋转。但是WA,终于知道为什么了,如果一个区间第一次rot+1,然后旋转了,如果又旋转这个区间,rot+1变成偶数,没旋转。实际上这个区间没旋转,而我这个却旋转了一次。。。也就是判断rot奇偶的时候必须这个区间以前没有旋转过。。

  后来我把旋转函数没放在maintain里面,maintain函数的功能就是不包括根节点只包括子节点的旋转操作,结果我又在pushdown的时候maintain了子节点。。(如果把旋转推到子节点,maintain子节点以后子节点又变成了旋转之前的。。),所以只有在把这个点的rot都推下去以后才能maintain这个点。

  区间合并的maintain函数都很恶心的。。。要仔细写。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 100010
#define MAXNODE 4*MAXN
using namespace std;
int T,N,M,rot[MAXNODE],li[MAXNODE],ld[MAXNODE],ri[MAXNODE],rd[MAXNODE],si[MAXNODE],sd[MAXNODE],lz[MAXNODE],rz[MAXNODE];
char a[MAXN];
void rotate(int o){
    rot[o]++;
    swap(si[o],sd[o]);
    swap(li[o],ld[o]);
    swap(ri[o],rd[o]);
    lz[o]=9-lz[o];
    rz[o]=9-rz[o];
}
void maintain(int o,int L,int R){
    if(L<R){
        int lc=(o<<1),rc=(o<<1|1),mid=(L+R)>>1;
        si[o]=max(si[lc],si[rc]);
        sd[o]=max(sd[lc],sd[rc]);
        li[o]=li[lc];
        ld[o]=ld[lc];
        ri[o]=ri[rc];
        rd[o]=rd[rc];
        lz[o]=lz[lc];
        rz[o]=rz[rc];
        if(lz[rc]>=rz[lc]){
            si[o]=max(si[o],ri[lc]+li[rc]);
            if(li[lc]==mid-L+1) li[o]+=li[rc];
            if(ri[rc]==R-mid) ri[o]+=ri[lc];
        }
        if(lz[rc]<=rz[lc]){
            sd[o]=max(sd[o],rd[lc]+ld[rc]);
            if(ld[lc]==mid-L+1) ld[o]+=ld[rc];
            if(rd[rc]==R-mid) rd[o]+=rd[lc];
        }
    }
}
void pushdown(int o){
    rotate(o<<1);
    rotate(o<<1|1);
    rot[o]=0;
}
void update(int o,int L,int R,int ql,int qr){
    if(ql<=L&&qr>=R){
        rotate(o);
        return;
    }
    else{
        if(rot[o]%2) pushdown(o);
        int lc=(o<<1),rc=(o<<1|1),mid=(L+R)>>1;
        if(ql<=mid) update(lc,L,mid,ql,qr);
        if(qr>mid) update(rc,mid+1,R,ql,qr);
    }
    maintain(o,L,R);
}
void build(int o,int L,int R){
    rot[o]=0;
    if(L==R){
        li[o]=ld[o]=ri[o]=rd[o]=si[o]=sd[o]=1;
        lz[o]=rz[o]=a[L]-'0';
        return;
    }
    else{
        int mid=(L+R)>>1;
        build(o<<1,L,mid);
        build(o<<1|1,mid+1,R);
    }
    maintain(o,L,R);
}
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&M);
        scanf("%s",a+1);
        build(1,1,N);
        while(M--){
            int ql,qr;
            char str[10];
            scanf("%s",str);
            if(str[0]=='f'){
                scanf("%d%d",&ql,&qr);
                update(1,1,N,ql,qr);
            }
            else printf("%d\n",si[1]);
        }
        puts("");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值