题解: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;
}
posted @ 2025-09-08 10:48  幻琳  阅读(5)  评论(0)    收藏  举报