CF145E Lucky Queries

题目描述

Petya loves lucky numbers very much. Everybody knows that lucky numbers are positive integers whose decimal record contains only the lucky digits 4 and 7. For example, numbers 47, 744, 4 are lucky and 5, 17, 467 are not.

Petya brought home string s s s with the length of n n n . The string only consists of lucky digits. The digits are numbered from the left to the right starting with 1 1 1 . Now Petya should execute m m m queries of the following form:

  • switch l l l r r r — "switch" digits (i.e. replace them with their opposites) at all positions with indexes from l l l to r r r , inclusive: each digit 4 4 4 is replaced with 7 7 7 and each digit 7 7 7 is replaced with 4 4 4 (1<=l<=r<=n) (1<=l<=r<=n) (1<=l<=r<=n) ;
  • count — find and print on the screen the length of the longest non-decreasing subsequence of string s s s .

Subsequence of a string s s s is a string that can be obtained from s s s by removing zero or more of its elements. A string is called non-decreasing if each successive digit is not less than the previous one.

Help Petya process the requests.

输入输出格式

输入格式:

The first line contains two integers n n n and m m m ( 1<=n<=106,1<=m<=3⋅105 1<=n<=10^{6},1<=m<=3·10^{5} 1<=n<=106,1<=m<=3105 ) — the length of the string s s s and the number of queries correspondingly. The second line contains n n n lucky digits without spaces — Petya's initial string. Next m m m lines contain queries in the form described in the statement.

输出格式:

For each query count print an answer on a single line.

输入输出样例

输入样例#1: 复制
2 3
47
count
switch 1 2
count
输出样例#1: 复制
2
1
输入样例#2: 复制
3 5
747
count
switch 1 1
count
switch 1 3
count
输出样例#2: 复制
2
3
2

说明

In the first sample the chronology of string s s s after some operations are fulfilled is as follows (the sought maximum subsequence is marked with bold):

  1. 47
  2. 74
  3. 74

    In the second sample: 1. 747

  4. 447
  5. 447
  6. 774
  7. 774

题解

一看数据范围就知道是线段树

不过怎样区间翻转是一个问题

这里的思想非常巧妙

记录最长不上升子序列的长度

在区间翻转的时候,交换最长不上升子序列的长度和最长不下降子序列的长度即可.

还有合并区间时需要用到一点dp的思想

代码有点丑神犇勿喷

code:

#include <iostream>
#include <cstdio>
#define MAXN 1000005
#define ll int
using namespace std;
struct node{
    int sheng,jiang,seven,four;
    bool lazy;
}tree[MAXN*4];
int max(int a,int b){
    return a>b?a:b;
}
int n,m;
void swap(int &a,int &b){
    int t=a;
    a=b;
    b=t;
}
char str[MAXN];
void push_up(int i){
    tree[i].seven=tree[i<<1].seven+tree[i<<1|1].seven;
    tree[i].four=tree[i<<1].four+tree[i<<1|1].four;
    int max1=max(tree[i<<1].sheng+tree[i<<1|1].seven,tree[i<<1].four+tree[i<<1|1].sheng);
    tree[i].sheng=max(max1,tree[i<<1].four+tree[i<<1|1].seven);
    //可能前面不下降,后面全7
    //可能前面全4,后面不下降
    //可能前面全4,后面全7
    int max2=max(tree[i<<1].jiang+tree[i<<1|1].four,tree[i<<1].seven+tree[i<<1|1].jiang);
    tree[i].jiang=max(max2,tree[i<<1].seven+tree[i<<1|1].four);
    //同理
}
void buildtree(int l,int r,int i){
    if (l==r){
        tree[i].sheng=1;
        tree[i].jiang=1;
        tree[i].seven=(str[l]=='7');
        tree[i].four=!tree[i].seven;
        return ;
    }
    int mid=(l+r)>>1;
    buildtree(l,mid,i<<1);
    buildtree(mid+1,r,i<<1|1);
    push_up(i);
}
void rev(int i){
    tree[i].lazy=!tree[i].lazy;
    swap(tree[i].four,tree[i].seven);
    swap(tree[i].jiang,tree[i].sheng);
    //区间翻转需要swap一下
}
void update(int l,int r,int L,int R,int i){
    if (r<L||l>R){
        return ;
    }
    if (r<=R&&l>=L){
        rev(i);
        return ;
    }
    if (tree[i].lazy){
        tree[i].lazy=false;
        rev(i<<1);
        rev(i<<1|1);
    }
	int mid=(l+r)>>1;
	if (mid>=r){
		update(l,mid,L,R,i<<1);
	}
    else if (l>mid){
    	update(mid+1,r,L,R,i<<1|1);
	}
	else {
		update(l,mid,L,R,i<<1);
		update(mid+1,r,L,R,i<<1|1);
	}
    push_up(i);
}
int main(){
    scanf("%d%d%s",&n,&m,str+1);
    buildtree(1,n,1);
    int ans;
    for (int t=0;t<m;t++){
        char ch[100];
        scanf("%s",ch);
        if (ch[0]=='c'){
            printf("%d\n",tree[1].sheng);
        }
        else {
            int l,r;
            scanf("%d%d",&l,&r);
            update(1,n,l,r,1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值