补题: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;
}