AtCoder Regular Contest 175(A~B)

补题:A - Spoon Taking Problem

阅读理解就能劝退好多人,先看B题可能收益会更高。

N个人都是这么坐的,勺子标号也给你标好了。

如果 s[1]=L,那么1这个人就要拿左边的勺子,如果左边没有就拿右边的,右边也没有就无解。

s[1]=R,也同理

如果s[1]=?,那么代表这个人左手和右手由我们来决定。

如果第一个人拿了右边,那么接下来所有人都必须拿右边,否则一定无解。

那么考虑s[1]=?,如果有一开始拿的是右边,而且轮到?的这个人,只有右边这个勺子,那么这个人是左手和右手都没有关系,总方案就会*2

然后模拟所有情况。

注意给了p序列决定了拿勺子的顺序,所以第一个人 s[p[1]],不是s[1]

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;
const int N=2e5+5,MOD=998244353;

int n,p[N];
string s;

int doL(){
    int ans=1;

    bool vis[n+5];
    per(i,0,n+1)vis[i]=false;
    vis[0]=vis[n+1]=true;
    vis[p[1]]=true;

    per(i,2,n){
        //接下来都拿左手
        int l=p[i],r=p[i]+1;
        if(r==n+1)r=1;

        if(vis[l] and vis[r]){//没勺子拿了
            return 0;
        }else
        if(!vis[l] and !vis[r]){//两个勺子都还在
            if(s[l]=='R'){//拿右无解
                return 0;
            }//剩下情况都拿左手
            vis[l]=true;
        }else
        if(!vis[l]){//只有左勺子
            vis[l]=true;
            if(s[l]=='?'){
                ans*=2;
                ans%=MOD;
            }
        }else{//只有右勺子
            return 0;
        }
    }

    return ans;
}

int doR(){
    int ans=1;

    bool vis[n+5];
    per(i,0,n+1)vis[i]=false;
    vis[0]=vis[n+1]=true;
    if(p[1]+1==n+1)vis[1]=true;
    else vis[p[1]+1]=true;

    per(i,2,n){
        //接下来都拿右手
        int l=p[i],r=p[i]+1;
        if(r==n+1)r=1;

        if(vis[l] and vis[r]){//没勺子拿了
            return 0;
        }else
        if(!vis[l] and !vis[r]){//两个勺子都还在
            if(s[l]=='L'){//拿左无解
                return 0;
            }//剩下情况都拿右手
            vis[r]=true;
        }else
        if(!vis[r]){//只有右勺子
            vis[r]=true;
            if(s[l]=='?'){
                ans*=2;
                ans%=MOD;
            }
        }else{//只有左勺子
            return 0;
        }
    }

    return ans;
}

void solve(){
    cin>>n;
    per(i,1,n)cin>>p[i];
    cin>>s;
    s="0"+s;

    int ans;
    if(s[p[1]]=='L')ans=doL();
    else if(s[p[1]]=='R')ans=doR();
    else ans=doL()+doR();

    cout<<ans%MOD<<endl;
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    while(t--)solve();
    return 0;
}

B - Parenthesis Arrangement

给你一个括号序列S,你可以进行两个操作:

1、交换 s[i],s[j]    花费 A

2、修改 s[i] 成为 '(' 或者 ')'     花费B

输出最少的花费,使得括号序列S成为合法的括号序列(每一个左括号都对应一个右括号)

首先,已经合法的括号就不需要修改了,这样的操作是多余的。

遍历括号序列,进栈,如果遇到栈顶是 '(',当前是 ')',那就弹出栈顶,否则当前入栈(去除合法括号)

然后就会剩下 ①(((((  或者 ②))))))  或者 ③))(((( 这三种情况。

显然①和②只能修改一半使序列合法。

③交换一次相当于修改两个位置。如果 a<=2*b,我们一定要先执行交换操作。

模拟一下若有 ))((((,交换一次 ()(((),显然一次交换产生了两对合法的,而不是一对。

交换完毕之后如果还有剩下的,((((,那么只需要将一半翻转。

如果a>2*b,那么我们只能翻转。

)))(((,左半边翻转一半,右半边翻转一半,如果是奇数还剩下两个括号 )(,那么还需要再翻两遍。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

//交换 i,j 花费 A
//把 i 换成 (或者) 花费B

//最少花费,让S变成合法的括号序列


void solve(){
    int n,a,b;
    cin>>n>>a>>b;
    string s;
    cin>>s;

    stack<char>sta,sta1;
    sta.push(s[0]);
    per(i,1,s.length()-1){
        if(sta.size() and sta.top()=='(' and s[i]==')'){
            sta.pop();
        }else{
            sta.push(s[i]);
        }
    }

    int c9=0,c0=0;
    while(sta.size()){
        if(sta.top()=='(')c9++;
        else c0++;
        sta1.push(sta.top());
        sta.pop();
    }

    int ans=0;
    if(c9==0 or c0==0){
        ans=(c9+c0)/2*b;
    }else{
        //  )))(((((

        if(a<=2*b){
            ans=(min(c0,c9)+1)/2*a+(max(c0,c9)-min(c0,c9))/2*b;
        }else{//全部修改
            if(c0&1){
                ans=(c0/2+c9/2+2)*b;
                // )(
            }else{//偶数
                ans=(c0/2+c9/2)*b;
            }
        }
    }

    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    while(t--)solve();
    return 0;
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值