省选联考 2024 D2T1 题解
省选联考 2024 D2T1
前言
Day1 发挥不完全,Day2 完全不发挥。
Day2 场上写这题写了 4h 25min,并且最终也只写了个假做法,最后两分钟在 T2 输出了 1 ,并获得了总分 5 分的好成绩。
赛后又瞪了两个小时终于做出来了这题,但是只能来写个题解了,只能说菜就多练。
这是本蒟蒻的第一篇洛谷题解,写此题解纪念这道葬送了我一整场省选 Day2 的题。
第一篇题解,表达能力不佳,请见谅。
题解
首先题目要访问序列的字典序最小,意思就是直接让你从前往后贪心。
下文中用选择一个点表示唤醒这个点的石像守卫。
所以 Bob 显然会先让第一个数最小,然后第二个,然后再是后面的。Alice 要做的是把 Bob 卡到最大。
先考虑第一个数怎么选。
首先发现答案具有单调性,所以可以二分第一个数是
那么 Alice 能做到的充要条件是能够使对于每一个
可以使用一次树形 dp 在
因为优先使第一个最大,所以我们二分出代价
然后题目要求 Bob 是以 dfs 的方式访问整个树,所以下一步肯定是访问
因为是二分,所以
还是因为在前面的优先,所以当访问
而
对于
(好好理解一下这里,赛场上就是没想明白这里然后就寄了,做这种题一定要彻底想明白再开始写)。
然后把
到这里这题就做完了,下面是时间复杂度的证明。
设
设
证毕。
时间复杂度
500ms不知道在卡什么
代码
非常好写,但是真的难想。
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pb push_back
#define pii pair<ll,ll>
#define fi first
#define se second
using namespace std;
const ll N=2e5+2,P=1e12+1;
ll T,n,m,k,a[N],b[N];
pii c[N];
ll dfs(ll x,ll h){
if(x>=(1<<n))return(a[x]<h?P:0);
ll p=dfs(x<<1,h),q=dfs(x<<1|1,h);
return p+min(a[x],q);
}
void Dfs(ll x,ll h){
if(x>=(1<<n)){
if(a[x]<h)m-=a[x>>1];
return b[++k]=a[x],void();
}
ll l=x,r=x,f=0;
while(l<(1<<n))l<<=1;
while(r<(1<<n))r=(r<<1|1);
for(ll i=l;i<=r;i++)c[++f]={a[i],i};
sort(c+1,c+f+1),l=1,r=f;
ll p=0,s=0;
while(l<=r){
ll mid=l+r>>1;
ll v=dfs(x,max(c[mid].fi,h));
if(x&(x>1&&c[mid].fi<h))
v=min(v,dfs(x,c[mid].fi)+a[x>>1]);
if(v>m)r=mid-1;
else l=mid+1,p=c[mid].se,s=v;
}
m-=s,b[++k]=h=a[p];
do
m+=min(p&1?P:a[p>>1],dfs(p^1,h)),
Dfs(p^1,h),p>>=1;
while(p!=x);
}
void slv(){
cin>>n>>m,k=0;
for(ll i=1;i<(1<<n+1);i++)cin>>a[i];
Dfs(1,0);
for(ll i=1;i<=k;i++)cout<<b[i]<<' ';
cout<<'\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)slv();
return 0;
}