题解:P13540 [IOI 2025] Obstacles for a Llama
下面是 vp 时的做法,由于一些原因可能做复杂了。
首先看上去非常的奇怪,我们关注一下限制最松的一部分,也就是
考虑对这些列做分治,也就是将区间
考虑区间之间的连边,注意到区间
注意到区间
所以我们会把集合
再考虑
当递归完
写一发,交上去发现过不了。
你发现漏掉了
到这里可以过掉最后一个子任务之前的所有子任务。
考虑正解,不妨把合并过程建树,然后把每个点在合并过程中强制拐弯的位置刻画出来。
所谓强制拐弯,就是如果
在处理递归完返回的
查询就在由合并建立的树上在链查询标记
细节比较多,可以看代码。
事实上,因为我的做法建出的是广义笛卡尔树所以有较多的分类讨论,但注意到相同值并不需要特殊处理,所以可以直接建一般笛卡尔树(也就是每次随意取出一个最小值位置去分治)减少很多细节。
下面贴出一般笛卡尔树写法的代码:
#include<bits/stdc++.h>
#include "obstacles.h"
#define fir first
#define sec second
using namespace std;
const int maxn = 6e5+114;
int t[maxn],h[maxn],n,m;
int fa[maxn];
int mx[maxn<<2];
pair<int,int> mi[maxn<<2];
void build(int cur,int lt,int rt){
if(lt==rt){
mi[cur]=make_pair(h[lt],lt);
mx[cur]=h[lt];
return ;
}
int mid=(lt+rt)>>1;
build(cur<<1,lt,mid),build(cur<<1|1,mid+1,rt);
mi[cur]=min(mi[cur<<1],mi[cur<<1|1]);
mx[cur]=max(mx[cur<<1],mx[cur<<1|1]);
}
pair<int,int> askmi(int cur,int lt,int rt,int l,int r){
if(rt<l||r<lt) return make_pair(1e9+114,-1);
if(l<=lt&&rt<=r) return mi[cur];
int mid=(lt+rt)>>1;
return min(askmi(cur<<1,lt,mid,l,r),askmi(cur<<1|1,mid+1,rt,l,r));
}
int askmx(int cur,int lt,int rt,int l,int r){
if(rt<l|r<lt) return -1;
if(l<=lt&&rt<=r) return mx[cur];
int mid=(lt+rt)>>1;
return max(askmx(cur<<1,lt,mid,l,r),askmx(cur<<1|1,mid+1,rt,l,r));
}
int find(int u){
return fa[u]=(fa[u]==u?u:find(fa[u]));
}
int premi[maxn],premx[maxn];
vector<int> E[maxn<<1];
int val[maxn<<1];
int jump[maxn][20],Mx[maxn][20],Mi[maxn][20];
int dep[maxn],lg[maxn];
int tot;
void dfs(int u,int lst){
if(lst==-1) dep[u]=1;
else dep[u]=dep[lst]+1;
jump[u][0]=lst;
Mx[u][0]=(val[u]==-1?-1e9:val[u]);
Mi[u][0]=(val[u]==-1?1e9:val[u]);
for(int i=1;i<20;i++){
if(jump[u][i-1]==-1){
jump[u][i]=-1;
Mx[u][i]=Mx[u][i-1];
Mi[u][i-1]=Mi[u][i-1];
}
else{
jump[u][i]=jump[jump[u][i-1]][i-1];
Mx[u][i]=max(Mx[u][i-1],Mx[jump[u][i-1]][i-1]);
Mi[u][i]=min(Mi[u][i-1],Mi[jump[u][i-1]][i-1]);
}
}
for(int nxt:E[u]){
dfs(nxt,u);
}
}
pair<int,int> solve(int l,int r){
//返回可以靠到最左边/右边的连通块
int pos=askmi(1,0,m-1,l,r).sec;
int v=h[pos];
if(v>=t[0]) return make_pair(-1,-1);
pair<int,int> res=make_pair(-1,-1);
if(pos!=l){
pair<int,int> son=solve(l,pos-1);
bool flag=false;
if(son.fir!=-1&&son.sec!=-1&&find(son.fir)==find(son.sec)) flag=true;
if(son.fir!=-1) res.fir=find(son.fir);
if(son.sec!=-1){
if(find(son.sec)!=find(pos)){
if(flag==false){
tot++;
E[tot].push_back(find(son.sec));
E[find(pos)].push_back(tot);
fa[find(son.sec)]=fa[tot]=find(pos);
val[tot]=pos;
}else{
tot++;
E[tot].push_back(find(son.sec));
E[tot].push_back(find(pos));
fa[find(son.sec)]=fa[find(pos)]=fa[tot]=tot;
val[tot]=-1;
}
}
}
}else res.fir=find(pos);
if(pos!=r){
pair<int,int> son=solve(pos+1,r);
bool flag=false;
if(son.fir!=-1&&son.sec!=-1&&find(son.fir)==find(son.sec)) flag=true;
if(son.fir!=-1){
if(find(son.fir)!=find(pos)){
if(flag==false){
tot++;
E[tot].push_back(find(son.fir));
E[find(pos)].push_back(tot);
fa[find(son.fir)]=fa[tot]=find(pos);
val[tot]=pos;
}else{
tot++;
E[tot].push_back(find(son.fir));
E[tot].push_back(find(pos));
fa[find(son.fir)]=fa[find(pos)]=fa[tot]=tot;
val[tot]=-1;
}
}
}
if(son.sec!=-1) res.sec=find(son.sec);
}else res.sec=find(pos);
int L=0,R=n;
while(L+1<R){
int mid=(L+R)>>1;
if(premi[mid]>v) L=mid;
else R=mid;
}
int edge=premx[L];
if(askmx(1,0,m-1,l,pos)<edge){
if(res.fir==-1) res.fir=find(pos);
else if(l!=0){
if(find(res.fir)!=find(pos)){
tot++;
E[tot].push_back(find(res.fir));
val[tot]=l-1;
tot++;
E[tot].push_back(tot-1);
E[tot].push_back(find(pos));
fa[find(res.fir)]=fa[tot-1]=fa[find(pos)]=fa[tot]=tot;
val[tot]=-1;
}
}
}
if(askmx(1,0,m-1,pos,r)<edge){
if(res.sec==-1) res.sec=find(pos);
else if(r!=m-1){
if(find(res.sec)!=find(pos)){
tot++;
E[tot].push_back(find(res.sec));
val[tot]=r+1;
tot++;
E[tot].push_back(tot-1);
E[tot].push_back(find(pos));
fa[find(res.sec)]=fa[tot-1]=fa[find(pos)]=fa[tot]=tot;
val[tot]=-1;
}
}
}
return res;
}
void initialize(std::vector<int> T, std::vector<int> H){
n=T.size(),m=H.size();
for(int i=0;i<n;i++) t[i]=T[i];
for(int i=0;i<m;i++) h[i]=H[i];
for(int i=0;i<m;i++) fa[i]=i;
build(1,0,m-1);
for(int i=0;i<m;i++) val[i]=i;
tot=m-1;
premi[0]=premx[0]=t[0];
for(int i=1;i<n;i++){
premi[i]=min(premi[i-1],t[i]);
premx[i]=max(premx[i-1],t[i]);
}
lg[1]=0;
for(int i=2;i<maxn;i++) lg[i]=lg[i>>1]+1;
solve(0,m-1);
for(int i=0;i<=tot;i++){
if(find(i)==i) dfs(i,-1);
}
}
bool can_reach(int L, int R, int S, int D){
if(find(S)!=find(D)) return false;
int mx=-1e9,mi=1e9;
if(dep[S]<dep[D]) swap(S,D);
while(dep[S]>dep[D]){
mx=max(mx,Mx[S][lg[dep[S]-dep[D]]]);
mi=min(mi,Mi[S][lg[dep[S]-dep[D]]]);
S=jump[S][lg[dep[S]-dep[D]]];
}
if(S!=D){
for(int i=19;i>=0;i--){
if(jump[S][i]!=jump[D][i]){
mx=max(mx,max(Mx[S][i],Mx[D][i]));
mi=min(mi,min(Mi[S][i],Mi[D][i]));
S=jump[S][i],D=jump[D][i];
}
}
mx=max(mx,max(Mx[S][0],Mx[D][0]));
mi=min(mi,min(Mi[S][0],Mi[D][0]));
S=jump[S][0],D=jump[D][0];
}
mx=max(mx,Mx[S][0]);
mi=min(mi,Mi[S][0]);
if(mx>R||mi<L) return false;
return true;
}