我想的这样一种办法:
对于题目给出的树,维护每个节点能被(该子树内)排名多少的叶节点达到,从下向上合并。
然后发现似乎都是一段连续区间,于是只维护左右界。
然后,把子树内排名映射到全局可用排名,从下往上拿线段树做一下区间覆盖,最后得出最多能进第几局比赛。
想问的是这个思路是否可行。比赛代码合并的细节推理挂了,没整好。
//g++ c.cpp -o c -g -std=c++14 -O0 -Wall
#include<iostream>
#include<cstdio>
using namespace std;
int qd(){
int rt=0;char c=getchar();
while(c<'0'||c>'9') c=getchar();
while('0'<=c&&c<='9') rt=(rt<<3)+(rt<<1)+c-48,c=getchar();
return rt;
}
const int maxn=1e6+10;
int N,xN,M,a[maxn],g[maxn<<1],ans[maxn];
struct node{int l,r,sz;}f[maxn<<1];
void pushdown(int t){if(g[t]) g[t<<1]=max(g[t<<1],g[t]),g[t<<1|1]=max(g[t<<1|1],g[t]);}
void change(int t,int l,int r,int ul,int ur,int v){
if(r<ul||l>ur) return;
if(ul<=l&&r<=ur){g[t]=v;return;}
int m=(l+r)>>1;pushdown(t);
change(t<<1,l,m,ul,ur,v),change(t<<1|1,m+1,r,ul,ur,v);
}
void rbuild(int t,int l,int r){
if(l==r){ans[l]=g[t];return;}
int m=(l+r)>>1;pushdown(t);
rbuild(t<<1,l,m),rbuild(t<<1|1,m+1,r);
}
int main(){
N=qd(),M=qd();xN=1<<N;
for(int i=1;i<xN;i++) a[i]=qd();
for(int i=1;i<=xN;i++) f[i+xN-1]={1,1,1};
for(int i=xN-1;i;i--){
f[i].sz=(f[i<<1].sz<<1);
if(a[i]==1) f[i].l=max(f[i<<1].l,f[i<<1|1].l),f[i].r=max(f[i<<1].r,f[i<<1|1].r)+f[i<<1].sz;
else f[i].l=min(f[i<<1].l,f[i<<1|1].l),f[i].r=(f[i<<1].r+f[i<<1|1].r)/2;
if(a[i]==1&&f[i<<1].l==f[i<<1|1].l) f[i].l=f[i<<1].l+1;
}
for(int i=(xN<<1)-1;i;i--){
change(1,1,xN,f[i].l,xN-f[i].sz+f[i].r,__builtin_ffs(f[i].sz));
// printf("change %d %d %d\n",f[i].l,xN-f[i].sz+f[i].r,__builtin_ffs(f[i].sz));
}
rbuild(1,1,xN);
for(int i=1;i<=M;i++){
int x=qd(),y=qd();
printf(ans[x]>=y?"Yes\n":"No\n");
}
// for(int i=1;i<=xN;i++) printf("%d ",ans[i]);
// putchar('\n');
return 0;
}