爬塔(set、括号匹配)

题意

Alice最近迷上了杀怪爬塔的游戏,在塔中有\(n\)层关卡,在通过第\(i\)层关卡后Alice会走上第\(i+1\)层。每个关卡中可能会获得女神的祝福或者遇到怪物。如果得到女神的祝福,Alice生命值会\(+1\),如果遇到怪物生命值会\(-1\)。起初Alice的生命值为\(1\),他想要最后生命值仍然为\(1\)。现在给\(q\)组询问,每组询问给定\(L, R\),问在第\(L\)层到第\(R\)层Alice能通过的关卡数最多是多少?

注意Alice可以从\(L\sim R\)的任意一层出发,期间任何时刻Alice生命值不能为\(0\)

题目保证在\(1\sim n\)层中,无论Alice从哪一层开始挑战,他能通过的关卡数\(\leq 10\)

数据范围

\(1 \leq n \leq 10^5\)
\(1 \leq q \leq 10^5\)

思路

题目可以转换成遇到\(1\)表示\(+1\),遇到\(0\)表示\(-1\),求一段最长的区间使得区间和为\(0\),且他从起点开始的任一子区间和不能为负数。其实就是经典的最长括号匹配的问题。

因为题目限制了最长只有\(10\),那么直接用set分开保存所有长度为\(x(1\leq x \leq 10)\)的可以匹配的左端点即可。换句话来说,开一个大小为\(10\)的set,然后枚举每个区间\((l, r)\)。如果当前区间和为\(0\),且中间和不会降到负数,那么将\(l\)保存在set[\(r-l+1\)]的左端点集合。

对于每一个区间查询\((l,r)\),从大到小枚举匹配的长度\(x\),如果在对应匹配长度\(x\)的set中能找到\(\geq l\)的下标\(t\)(使用lower_bound即可),并且满足\(t + x - 1 \leq r\),那么\(x\)即为答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;

const int N = 100010;

int n, m;
char s[N];
set<int> st[15];

int main()
{
    scanf("%d", &n);
    scanf("%s", s + 1);
    for(int i = 1; i <= n; i ++) {
        int tmp = 1;
        for(int j = i; j <= min(n, i + 9); j ++) {
            if(s[j] == '1') tmp ++;
            else tmp --;
            if(tmp <= 0) break;
            if(tmp == 1) st[j - i + 1].insert(i);
        }
    }
    scanf("%d", &m);
    while(m --) {
        int l, r;
        scanf("%d%d", &l, &r);
        bool flag = false;
        for(int i = 10; i >= 1; i --) {
            auto t = st[i].lower_bound(l);
            if(t == st[i].end()) continue;
            if(*t >= l && (*t) + i - 1 <= r) {
                printf("%d\n", i);
                flag = true;
                break;
            }
        }
        if(!flag) printf("0\n");
    }
    return 0;
}
posted @ 2022-03-31 19:01  pbc的成长之路  阅读(43)  评论(0编辑  收藏  举报