括号题
找个时间回顾一下。
P7914 括号序列
https://www.luogu.com.cn/problem/P7914
P8003 Hard Brackets Inserting
https://www.luogu.com.cn/problem/solution/P8003
CF1709C
题意:给定一个匹配括号序列,将部分位置变成问号,问是否有唯一的方案将所有问号变成左括号或右括号,使得该括号序列依然为匹配的。
分析:
首先我们考虑构造一组方案。先算出括号中左括号和右括号的位置。当左括号没用完的时候我们优先使用左括号。否则使用右括号。
为什么这么做呢?证明:因为题目保证一定存在一组合法的括号序列,故一定有一种合法的序列。假设这种方案(记为 \(S\))不成立,那么一定存在另一种方案(记为 \(T\))成立。并且这种方案一定是 \(S\) 中经历若干次左右括号交换而来的。考虑在 \(S\) 的某个位置 \(x\) 出现不成立的情况,说明 \(1 \sim x\) 中右括号数量大于左括号数量。考虑被交换的位置 \(y,z\),满足 \(S_y=\mathtt{'('},S_z=\mathtt{')'}\),则 \(y<z\) 一定成立。
- 如果 \(x<y<z\),那么交换之后,\(x\) 左边右括号和左括号数量不变,依然会在 \(x\) 处不成立。
- 如果 \(y<x<z\),那么交换之后,\(x\) 左边会多出一个右括号少掉一个左括号,更不会成立。
- 如果 \(y<z<x\),那么交换之后,\(x\) 左边右括号和左括号数量不变,依然会在 \(x\) 处不成立。
与假设矛盾,原命题成立。
然后我们考虑是否能构造出其他方案合法。考虑这个方案:将 \(S\) 中最后一个左括号和第一个右括号交换,依然类似的思路证明该方案次优。判断这种方案是否可行,如果可行那么 \(\mathtt{NO}\),否则 \(\mathtt{YES}\)。(如果不存在这种方案,也就是问号中没填左/右括号,一定 \(\mathtt{YES}\))。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int l, r, q, nl, nr;
stack<int> lft; queue<int> rgt;
bool check(string s){
int cnt=0;
f(i,0,(int)s.size()-1){if(s[i]=='(')cnt++;else cnt--;if(cnt<0)return 0;}
return 1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
int t; cin >> t;
while(t--) {
while(!lft.empty())lft.pop();while(!rgt.empty())rgt.pop();
string s; cin >> s; int n = s.size();l=r=0;
f(i, 0, (int)s.size() - 1) {
if(s[i]=='(')l++; else if(s[i]==')')r++;
}
nl=(n/2)-l, nr=(n/2)-r;
f(i, 0, (int)s.size() - 1) {
if(s[i]=='?'){
if(nl) {lft.push(i);s[i]='(';nl--;}
else {rgt.push(i);s[i]=')';nr--;}
}
}
if(lft.empty()||rgt.empty()) {cout<<"YES\n";continue;}
int x=lft.top(), y=rgt.front();
swap(s[x],s[y]); if(check(s)){cout<<"NO\n";} else {cout<<"YES\n";}
}
return 0;
}