[省选联考 2024] 迷宫守卫
C1942huangjiaxu · · 题解
考虑 Bob 只选一个叶子时怎么做。
那么 Alice 每选择一个点,就相当于把右子树删掉,最后要使剩下数中的最小值最大。
考虑
转移有
直接记录状态是
转移可以用归并排序做到
求出
考虑如何利用
设计函数
一样找到最大的
如果
如果
这时候我们要考虑 Alice 是否选择
因为我们选择了
具体的,只有
判断出是否选
时间复杂度
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=(1<<17)+5;
const ll inf=1e13;
int T,n,tn,a[N],rv[N],b[N],cb,c[18][N];
ll w[N],m,f[18][N],fl[18][N],fr[18][N];
bool vw[18][N];
#define ls(x) ((x)<<1)
#define rs(x) ((x)<<1|1)
void dfs(int x,int l,int r,int d){
if(x>=tn){
f[d][l]=0,c[d][l]=a[x];
return;
}
int mid=l+r>>1;
dfs(ls(x),l,mid,d+1),dfs(rs(x),mid+1,r,d+1);
int i=l,j=mid+1,k=l;
ll vl=0,vr=0;
while(i<=mid&&j<=r){
if(c[d+1][i]<c[d+1][j]){
c[d][k]=c[d+1][i];
vl=f[d+1][i];
vr=f[d+1][j];
++i;
}else{
c[d][k]=c[d+1][j];
vr=f[d+1][j];
vl=f[d+1][i];
++j;
}
fl[d][k]=vl,fr[d][k]=vr;
if(w[x]<vr)vw[d][k]=true;
else vw[d][k]=false;
f[d][k]=min(inf,fl[d][k]+min(w[x],fr[d][k]));
++k;
}
while(i<=mid){
c[d][k]=c[d+1][i];
vl=f[d+1][i];
vr=inf;
++i;
fl[d][k]=vl,fr[d][k]=vr;
if(w[x]<vr)vw[d][k]=true;
else vw[d][k]=false;
f[d][k]=min(inf,fl[d][k]+min(w[x],fr[d][k]));
++k;
}
while(j<=r){
c[d][k]=c[d+1][j];
vr=f[d+1][j];
vl=inf;
++j;
fl[d][k]=vl,fr[d][k]=vr;
if(w[x]<vr)vw[d][k]=true;
else vw[d][k]=false;
f[d][k]=min(inf,fl[d][k]+min(w[x],fr[d][k]));
++k;
}
}
ll calc(int x,int l,int r,ll rs,int d){
if(l==r){
b[++cb]=a[x];
return 0;
}
int mid=l+r>>1;
int j=r;
while(j>l&&fl[d][j]+min(w[x],fr[d][j])>rs)--j;
ll res=0;
if(rv[c[d][j]]<=mid){
res=calc(ls(x),l,mid,rs-min(w[x],fr[d][j]),d+1);
if(vw[d][j]&&rs-fr[d][j]<res)res+=w[x];
rs-=res;
res+=calc(rs(x),mid+1,r,rs,d+1);
}else{
res=calc(rs(x),mid+1,r,rs-fl[d][j],d+1);
rs-=res;
res+=calc(ls(x),l,mid,rs,d+1);
}
return res;
}
void solve(){
scanf("%d%lld",&n,&m);
tn=1<<n,cb=0;
for(int i=1;i<tn;++i)scanf("%lld",&w[i]);
for(int i=0;i<tn;++i){
scanf("%d",&a[i+tn]);
rv[a[i+tn]]=i;
}
dfs(1,0,tn-1,0);
calc(1,0,tn-1,m,0);
for(int i=1;i<=tn;++i)printf("%d%c",b[i]," \n"[i==tn]);
}
int main(){
scanf("%d",&T);
while(T--)solve();
return 0;
}