版权声明:转载请在原文附上源连接以及作者,谢谢~ https://blog.csdn.net/weixin_39778570/article/details/83714901
ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
Princess Principal
题目:https://ac.nowcoder.com/acm/contest/201/J
题意:使用数组代表括号,0,1为一对左右括号,1,2为另一对,3,4。。。同理。给定n个括号,查询m次[l,r]范围是否匹配合法。
解法:查询次数比较多,使用一个数组记录括号匹配情况,topp[i]表示从i开始往左边第一个匹配不上的位置,那么[L,r]区间是否匹配可以转换为topp[L-1]是否等于topp[r],若相等则说明区间[l,r]可以完全匹配,因为topp[r]一定往左越过了L,括号是否匹配的话,依然用一个栈来维护,匹配上了就把左括号出栈,匹配不上就进栈,左括号,直接进栈。栈顶一定为当前位置往左第一个匹配不上的位置
解法二:仍然用栈来存储,由于匹配方式是唯一的,我们用一个数组 b[i] 来记录第 i个括号所匹配的括号的位置。若一个区间内的括号完全匹配,当我们从区间内取出一个单括号时,它所匹配的括号也必然在这个区间内,这种性质称为“封闭性”。
然后我们预处理每个括号所匹配括号的位置即b[i],但是查询的时候复杂度仍然是线性的。然而我们只需要对区间(l,r)求所匹配的括号位置的最值进行查询即可,换言之,若区间(l,r)所匹配的括号的最大值或者最小值不在(l,r)里,那么就违反了“封闭性”。问题就转换成了求区间最值,我们可以采用RMQ或线段树解决。
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e6+5;
int n,m,q;
ll a[maxn],ttop[maxn];//a表示括号类型
void pend(){
stack<ll> st; // 存放下标
ttop[0] = 0; // 初始状态,可以理解为第0个不匹配
fo(i,1,n){ // 0,1 2,3 4,5 为不同括号(第0,1,2种)
if(st.empty() || !(a[i]&1)) st.push(i); // 如果栈空,或者遇到左括号加入栈
else{
if(a[i] == a[st.top()]+1) st.pop(); // 右括号匹配上了左括号,去掉左括号
else st.push(i); // 匹配不上加(下标) 入栈
}
if(st.empty()) ttop[i] = 0; // 栈空表示中间都能匹配,都是合法的
else ttop[i] = st.top(); // 匹配不上的情况下ttop[i]=i, 匹配上了ttop[i]=往左边第一个匹配不上的下标
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
fo(i,1,n) scanf("%lld",&a[i]);
pend();
int l,r;
while(q--){
scanf("%d%d",&l,&r);
if(ttop[l-1]==ttop[r])puts("Yes"); // 表示第l-1个和第r个往左第一个匹配不上的下标相同
else puts("No"); // 如果相同则说明了r这个括号匹配不上的下标已经跳过了[l,r]这个范围,说明[l,r]完全匹配
}
}
解法二:ST算法
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e6+5;
int n,m,q;
int a[maxn], b[maxn]; // b数组储存匹配上的括号的下标
stack<int> st;
int dp_max[maxn][20],dp_min[maxn][20]; // 数组大小maxn, log(n)/log(2)
// dp[i][j] 表示 [i,i+2^j -1] 的最值
void ST(){
fo(i,1,n){
dp_max[i][0] = b[i];
dp_min[i][0] = b[i];
}
int t = log(n)/log(2);
fo(j,1,t){
for(int i=1; i+(1<<j)-1<=n; i++){
dp_max[i][j] = max(dp_max[i][j-1], dp_max[i+(1<<(j-1))][j-1]);
dp_min[i][j] = min(dp_min[i][j-1], dp_min[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ_min(int l, int r){
int k = log(r-l+1)/log(2);
return min(dp_min[l][k], dp_min[r-(1<<k)+1][k]);
}
int RMQ_max(int l, int r){
int k = log(r-l+1)/log(2);
return max(dp_max[l][k], dp_max[r-(1<<k)+1][k]);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
fo(i,1,n){
scanf("%d",&a[i]);
b[i] = -1;
if(st.empty() || !(a[i]&1))st.push(i);
else{
if(a[i] == a[st.top()]+1){
// 两个位置都要匹配上
b[i] = st.top();
b[st.top()] = i;
st.pop();
}else st.push(i);
}
}
ST();
int L,R;
fo(i,1,q){
scanf("%d%d",&L,&R);
if(RMQ_min(L,R)>=L && RMQ_max(L,R)<=R) puts("Yes");
else puts("No");
}
return 0;
}