题解:P8430 [COI 2020] Zagrade
思路
括号匹配,首先想到使用栈。
对于 Subtask 1 与 Subtask 3,遍历一遍 \(1 \sim n\),每次将此次遍历到的下标加入栈,如果栈的大小 \(\ge 2\),取出最上方的两个下标并询问是否合法,如果合法,那么必然是 (...),的情况,记录并弹出栈。
对于为什么必然是 (...) 的情况,之后会有一点证明。
接下来考虑不合法的括号序列。
如果总序列不合法,那么栈一定不空,设 \(m\) 为栈中剩下的元素。
由于每次匹配都消耗 ( 和 ) 各 \(1\) 个,剩下 \(m\) 个括号中必然有 ( 和 ) 各 \(\frac{m}{2}\) 个。
对于前 \(\frac{m}{2}\) 个括号,一定与后 \(\frac{m}{2}\) 个不形成匹配,这时,我们可以对这前 \(\frac{m}{2}\) 个进行分类讨论,为了方便,这里仅对第 \(\frac{m}{2}\) 个括号进行讨论。
- 当第 \(\frac{m}{2}\) 个括号为
(时,后 \(\frac{m}{2}\) 个括号不能为),即只能为(,此时(有 \(\frac{m}{2}+1\) 个,不合法。 - 当第 \(\frac{m}{2}\) 个括号为
)时,前 \(\frac{m}{2}-1\) 个括号不能为(,即只能为),此时(为 \(\frac{m}{2}\) 个,合法。
综上所述,对于栈不空的情况,有且只有一种情况,为前 \(\frac{m}{2}\) 个括号为 ) 且后 \(\frac{m}{2}\) 个括号为 (。
还有一些小细节,可以在讨论区寻找。
证明
对于栈中合法匹配必然为 (...) 的情况,首先,第一次匹配成功的情况必然为 \(x\) 与 \(x+1\),即最小的括号匹配 ()。之后,\(x\) 与 \(x+1\) 弹出,如果之后又有成功匹配,要么包含之前成功匹配过的序列,要么是最小括号匹配。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,Q;
char ans[N];
int ask(int l,int r){
int Ask;
cout<<"? "<<l<<' '<<r<<endl;
cin>>Ask;
return Ask;
}
signed main(){
cin>>n>>Q;
stack<int> s;
for(int i=1;i<=n;i++){
s.push(i);
if(s.size()>=2){
int r=s.top();
s.pop();
int l=s.top();
s.pop();
if(ask(l,r)){
ans[l]='(';
ans[r]=')';
}else{
s.push(l);
s.push(r);
}
}
}
int sn=s.size();
for(int i=1;i<=sn/2;i++){
ans[s.top()]='(';
s.pop();
}
while(!s.empty()){
ans[s.top()]=')';
s.pop();
}
cout<<"! "<<ans+1<<endl;
return 0;
}

浙公网安备 33010602011771号