牛客国庆训练,CCPC Camp DAY1 J 倍增,括号匹配
https://www.nowcoder.com/acm/contest/201#question
题意:中文不翻译了
解法的个人理解:
对于一个合法的区间$[L,R]$
1.显然其左括号的匹配位置都小于等于$R$,其右括号的匹配位置都大于等于$L$,
2.左括号和右括号数量相同
3.区间的长度为偶数
后面两点是必要的,但是不够充分
第一点是最关键的,我们可以考虑转化
如果将整个序列左括号所匹配的位置记录下来作为数列$a_i$,同理右括号记为$b_i$原本的询问就等于是询问一个区间最大值和区间最小值了
即$a_i$的最大值小于$R$,$b_i$的最小值小于$L$,至于具体怎么查,用st表,线段树,树状数组都行
而实际上,在满足2,3条件之后,最大值和最小值只用计算其中一个即可
例如满足了左括号的匹配位置都小于$R$,那么区间内的左括号都已经找到归属了,换句话说只要左括号和右括号数目相等,则必然合法
代码如下:
#include <bits/stdc++.h> #define rep(ii,a,b) for(register int ii=a;ii<=b;++ii) #define per(ii,a,b) for(register int ii=b;ii>=a;--ii) using namespace std; const int maxn=2e6+10,maxm=2e6+10; int casn,n,m,k,q,a[maxn],stk[maxn]; int l[maxn],sum[maxn],st[maxn][22],top,ll,rr; inline int rmax(int ll,int rr){ int len=0; while(ll+(1<<len)-1<=rr) len++; len--; return max(st[ll][len],st[rr-(1<<len)+1][len]); } int main() { scanf("%d%d%d",&n,&m,&q); rep(i,1,n) scanf("%d",&a[i]); rep(i,1,n) if(a[i]%2) sum[i]=sum[i-1]-1; else sum[i]=sum[i-1]+1; per(i,1,n) if(a[i]%2)stk[++top]=i; else{ if(top&&a[i]/2==a[stk[top]]/2){ l[i]=stk[top--]; }else { l[i]=maxn+10; top=0; } } rep(i,1,n) st[i][0]=l[i]; rep(i,1,21) rep(j,1,n) st[j][i]=max(st[j][i-1],st[min(j+(1<<(i-1)),n)][i-1]); while(q--){ scanf("%d%d",&ll,&rr); if((rr-ll+1)%2==0&&sum[rr]==sum[ll-1]&&rmax(ll,rr)<=rr) puts("Yes"); else puts("No"); } return 0; }