Princess Principal(多括號匹配,區間合法查詢)
阿新 • • 發佈:2018-11-21
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;
}