题解 - 括号序列

题目描述

白浅妹妹在玩打怪游戏,游戏共有 n个关卡,每通过一个关卡就会遇到一把武器,它的代号为ai,表示当你第ai次遇到代号为ai的武器时,才能够获得这把武器(代号相同的武器可以认为是相同的武器)。
现在有 m 次询问,每次指定一个关卡区间 [L,R],在通过这些关卡之后(白浅妹妹是一个高手,所以这些关卡都能通过),白浅妹妹需要从获得的武器中选出ki个(保证 ki≤ 4 )来与怪物对决,你需要输出你有多少种组合方案。

输入

第一行输入一个整数 n 表示关卡的数量。
第二行输入n个整数 ai(1 ≤ ai ≤ 109)表示第 i 个关卡遇到的武器的代号(保证任意两个武器的代号互不相同)。
第三行输入一个整数 m 表示挑战次数。
接下来的 m行,每行三个正整数Li, Ri, ki (1 ≤ Li ≤ Ri ≤ n, 1 ≤ ki ≤ 4) ,表示需要通过的关卡区间。

输出

输出m行,每行一个整数,表示该次挑战武器组合方案数量。

样例

样例输入

7
1 3 7 2 3 7 2
4
1 1 1
2 5 4
4 7 1
1 7 1

样例输出

1
0
1
2

提示

【样例解释】
对于第一个询问,获得的武器为 1 ,选出一把武器的方案为 (1)
对于第二个询问,没有获得的武器。
对于第三个询问,获得的武器为 2,选出一把武器的方案为 (2)
对于第四个询问,获得的武器为 1,2,选出一把武器的方案为 (1), (2) 两种。
【数据限制】
请添加图片描述

分析

相邻的非配对括号不能同色,

设第1个括号和第x个括号匹配,则可以将 [1,n] 分为 [1,x][x + 1,n]两部分,只需要满足第x个括号和第 x + 1 个括号不同色即可,因为可以想到 区间dp

f[l][r][x][y] 表示第l个括号涂x色,第r个括号涂y色时,区间[l,r]的最大代价 (其中用0,1,2分别代表不染色、染白色、染黑色)

最终结果即为 max({f[1][n][0][0],f[1][n][0][1],f[1][n][0][2],f[1][n][1][0],f[1][n][1][1],f[1][n][1][2],f[1][n][2][0],f[1][n][2][1],f[1][n][2][2]})

如果l和r配对,可以转移到 [l + 1,r - 1] ,此时x==y,故枚举x == 0,1,2即可

在这里插入图片描述

如果l和r不配对,找到l配对的右括号k,则可以转移到 [l,k] 和 [k + 1,r] ,枚举x和y的组合即可(9种)

在这里插入图片描述

此外,我们可以直接预处理出每个左括号所配对的右括号,通过实现

代码

#pragma GCC optimize(2)
  
#include<bits/stdc++.h>
   
using namespace std;
   
typedef long long LL;
   
const int N = 1000 + 10,M = 1e6 + 10;
 
int n,a,b;
string s;
int R[N];
int f[N][N][3][3];
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    cin >> n >> a >> b >> s;
     
    s = " " + s;
 
    stack<int> stk;
    for(int i = 1;i <= n;i++){
        if(s[i] == '(') stk.push(i);
        else{
            R[stk.top()] = i;
            stk.pop();
        }
    }
 
    for(int len = 2;len <= n;len += 2)
        for(int i = 1;i + len - 1 <= n;i++){
            int l = i,r = i + len - 1;
 
            if(s[l] == ')' || s[r] == '(') continue;
 
            if(len == 2){
                f[l][r][0][0] = 0;
                f[l][r][1][1] = a;
                f[l][r][2][2] = b;
            }else if(R[l] == r){
                f[l][r][0][0] = max({f[l + 1][r - 1][1][1],f[l + 1][r - 1][1][2],f[l + 1][r - 1][2][1],f[l + 1][r - 1][2][2]});
                f[l][r][1][1] = max({f[l + 1][r - 1][0][0],f[l + 1][r - 1][0][2],f[l + 1][r - 1][2][0],f[l + 1][r - 1][2][2]}) + a;
                f[l][r][2][2] = max({f[l + 1][r - 1][0][0],f[l + 1][r - 1][0][1],f[l + 1][r - 1][1][0],f[l + 1][r - 1][1][1]}) + b;
            }else{
                int k = R[l];
                f[l][r][0][0] = max(f[l][k][0][0] + f[k + 1][r][1][0],f[l][k][0][0] + f[k + 1][r][2][0]);
                f[l][r][0][1] = max(f[l][k][0][0] + f[k + 1][r][1][1],f[l][k][0][0] + f[k + 1][r][2][1]);
                f[l][r][0][2] = max(f[l][k][0][0] + f[k + 1][r][1][2],f[l][k][0][0] + f[k + 1][r][2][2]);
                f[l][r][1][0] = max(f[l][k][1][1] + f[k + 1][r][0][0],f[l][k][1][1] + f[k + 1][r][2][0]);
                f[l][r][1][1] = max(f[l][k][1][1] + f[k + 1][r][0][1],f[l][k][1][1] + f[k + 1][r][2][1]);
                f[l][r][1][2] = max(f[l][k][1][1] + f[k + 1][r][0][2],f[l][k][1][1] + f[k + 1][r][2][2]);
                f[l][r][2][0] = max(f[l][k][2][2] + f[k + 1][r][0][0],f[l][k][2][2] + f[k + 1][r][1][0]);
                f[l][r][2][1] = max(f[l][k][2][2] + f[k + 1][r][0][1],f[l][k][2][2] + f[k + 1][r][1][1]);
                f[l][r][2][2] = max(f[l][k][2][2] + f[k + 1][r][0][2],f[l][k][2][2] + f[k + 1][r][1][2]);
            }
        }
 
    cout << max({f[1][n][0][0],f[1][n][0][1],f[1][n][0][2],f[1][n][1][0],f[1][n][1][1],f[1][n][1][2],f[1][n][2][0],f[1][n][2][1],f[1][n][2][2]});
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值