支配点对小记
支配点对
例题
给定一棵
n 个结点的树,有边权。有m 次询问,每次询问求有几个不同的\mathrm{dep}(\mathrm{LCA}(x,y)) ,其中l \le x,y \le r 。
看起来是一个非常难的问题。
对于相同
对于两个点对
我们称没有被其他点对支配的点对为支配对。则我们只需要考虑支配对的贡献。
考虑如何找支配对。
我们钦定一个
容易发现当
依次枚举
但是这样复杂度是
考虑
对于一个结点,因为重儿子子树结点数大于所有轻儿子,依次合并轻儿子的过程相当于启发式合并。轻儿子中所有结点所处集合的大小至少翻倍,故对于每一个结点,我们最多合并
这也说明了支配对的数量是
对于每个支配对及其贡献
我们希望
维护每种
复杂度是
#include<bits/stdc++.h>
#define int long long
#define rd read()
#define gc pa == pb && (pb = (pa = buf) + fread(buf, 1, 100000, stdin), pa == pb) ? EOF : *pa++
using namespace std;
static char buf[100000], * pa(buf), * pb(buf);
inline int read()
{
register int x=0,ss=1,s=gc;
while(!isdigit(s)&&s!='-')s=gc;
if(s=='-')ss=-1,s=gc;
while(isdigit(s))x=(x<<1)+(x<<3)+(s^48),s=gc;
return ss*x;
}
const int N=100005,M=500005;
int n,m,ans[M],son[N],siz[N],dep[N],pos[N];
map<int,int> mp;
vector<pair<int,int> > v[N],p[N],q[N];
//p存下所有支配对
set<int> s;
struct BIT
{
int c[N];
inline void add(int x,int y){while(x<=n+3)c[x]+=y,x+=(x&-x);}
inline int ask(int x){int s=0;while(x)s+=c[x],x^=(x&-x);return s;}
}T;
inline void get(int x,int w)
{
auto i=s.lower_bound(x);
if(i!=s.end())p[x].push_back({*i,w});
if(i!=s.begin())--i,p[*i].push_back({x,w});
}
inline void find(int x,int f,int w){get(x,w);for(auto [i,j]:v[x])if(i!=f)find(i,x,w);}
inline void add(int x,int f){s.insert(x);for(auto [i,j]:v[x])if(i!=f)add(i,x);}
inline void dfs(int x,int f)
{
siz[x]=1;
for(auto [i,j]:v[x])if(i!=f)
{
dep[i]=dep[x]+j,dfs(i,x),siz[x]+=siz[i];
if(siz[son[x]]<siz[i])son[x]=i;
}
}
inline void sol(int x,int f)
{
for(auto [i,j]:v[x])if(i!=f&&i!=son[x])sol(i,x),s.clear();
if(son[x])sol(son[x],x);
s.insert(x),p[x].push_back({x,dep[x]});
for(auto [i,j]:v[x])if(i!=f&&i!=son[x])find(i,x,dep[x]),add(i,x);
}
signed main()
{
n=rd,m=rd;
for(int i=1,x,y,w;i<n;i++)
{
x=rd,y=rd,w=rd;
v[x].push_back({y,w});
v[y].push_back({x,w});
}
dfs(1,0);
int cnt=0;
for(int i=1;i<=n;i++)
{
if(!mp[dep[i]])mp[dep[i]]=++cnt;
dep[i]=mp[dep[i]];
}
for(int i=1;i<=cnt;i++)pos[i]=n+1;
sol(1,0);
for(int i=1,l;i<=m;i++)l=rd,q[l].push_back({rd,i});
for(int i=n;i>=1;i--)
{
for(auto [j,k]:p[i])T.add(pos[k],-1),pos[k]=min(pos[k],j),T.add(pos[k],1);
for(auto [j,k]:q[i])ans[k]=T.ask(j);
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
*例题
给定一棵
n 个结点的树,有m 次询问,每次询问有多少个二元组\mathrm{L},\mathrm{R} ,满足l\le \mathrm{L}\le \mathrm{R}\le r ,且对任意\mathrm{L}\le a_x\le a_y\le \mathrm{R} ,有x,y 在树上的最近公共祖先z 满足\mathrm{L}\le a_z\le \mathrm{R} 。
考虑单个点对
设
1.对于
2.对于
3.对于
然后将所有点对拿下来做扫描线是
继续考虑哪些点对没有用。钦定一个
可以发现对于一个
然后将每个支配对对应的
维护区间最小值
每经过一个时刻,对于
然后复杂度是
#include<bits/stdc++.h>
#define ll long long
#define rd read()
#define gc pa == pb && (pb = (pa = buf) + fread(buf, 1, 100000, stdin), pa == pb) ? EOF : *pa++
using namespace std;
static char buf[100000], * pa(buf), * pb(buf);
inline int read()
{
unsigned register int x=0,s=gc;
while(!isdigit(s))s=gc;
while(isdigit(s))x=(x<<1)+(x<<3)+(s^48),s=gc;
return x;
}
const int N=200005;
int n,m,a[N],son[N],siz[N];
ll ans[N];
struct node{int x,y,z;};
vector<node> k[N];
vector<pair<int,int> > q[N];
vector<int> v[N];
set<int> s;
inline void insert(int x,int y,int z)
{
if(y<z)k[y].push_back({1,x,1}),k[z].push_back({1,x,-1});
if(z<x)k[y].push_back({z+1,x,1});
}
inline void dfs(int x,int f)
{
siz[x]=1;
for(int i:v[x])if(i!=f)
{
dfs(i,x),siz[x]+=siz[i];
if(siz[son[x]]<siz[i])son[x]=i;
}
}
inline void get(int x,int z)
{
auto i=s.lower_bound(x);
if(i!=s.end())insert(x,*i,z);
if(i!=s.begin())--i,insert(*i,x,z);
}
inline void find(int x,int f,int w){get(a[x],w);for(int i:v[x])if(i!=f)find(i,x,w);}
inline void add(int x,int f){s.insert(a[x]);for(int i:v[x])if(i!=f)add(i,x);}
inline void sol(int x,int f)
{
for(int i:v[x])if(i!=f&&i!=son[x])sol(i,x),s.clear();
if(son[x])sol(son[x],x);
s.insert(a[x]);
for(int i:v[x])if(i!=f&&i!=son[x])find(i,x,a[x]),add(i,x);
}
struct seg
{
int mn[N<<2],cnt[N<<2],tag[N<<2],htg[N<<2];ll sum[N<<2];
inline void pushup(int id)
{
int l=id<<1,r=id<<1|1;
mn[id]=min(mn[l],mn[r]),sum[id]=sum[l]+sum[r];
cnt[id]=cnt[l]*(mn[l]==mn[id])+cnt[r]*(mn[r]==mn[id]);
}
inline void push1(int id,int v){tag[id]+=v,mn[id]+=v;}
inline void push2(int id,int v){htg[id]+=v,sum[id]+=1ll*cnt[id]*v;}
inline void pushdown(int id)
{
if(tag[id])push1(id<<1,tag[id]),push1(id<<1|1,tag[id]),tag[id]=0;
if(htg[id])
{
if(mn[id]==mn[id<<1])push2(id<<1,htg[id]);
if(mn[id]==mn[id<<1|1])push2(id<<1|1,htg[id]);
htg[id]=0;
}
}
inline void build(int id,int l,int r)
{
if(l==r)return cnt[id]=1,void();
int mid=l+r>>1;build(id<<1,l,mid),build(id<<1|1,mid+1,r),pushup(id);
}
inline ll ask(int id,int l,int r,int x,int y)
{
if(x<=l&&y>=r)return sum[id];
int mid=l+r>>1,ans=0;pushdown(id);
if(x<=mid)ans+=ask(id<<1,l,mid,x,y);
if(y>mid)ans+=ask(id<<1|1,mid+1,r,x,y);
return ans;
}
inline void add(int id,int l,int r,int x,int y,int v)
{
if(x<=l&&y>=r)
{
if(v)return push1(id,v);
if(!mn[id])return push2(id,1);
return;
}
int mid=l+r>>1;pushdown(id);
if(x<=mid)add(id<<1,l,mid,x,y,v);
if(y>mid)add(id<<1|1,mid+1,r,x,y,v);
pushup(id);
}
}T;
signed main()
{
n=rd,m=rd;T.build(1,1,n);
for(int i=1;i<=n;i++)a[i]=rd;
for(int i=2;i<=n;i++)v[rd].push_back(i);
dfs(1,0),sol(1,0);
for(int i=1,l,r;i<=m;i++)l=rd,r=rd,q[r].push_back({l,i});
for(int i=1;i<=n;i++)
{
for(auto [l,r,w]:k[i])T.add(1,1,n,l,r,w);
T.add(1,1,n,1,i,0);
for(auto [l,fl]:q[i])ans[fl]=T.ask(1,1,n,l,i);
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
例题
给定一个序列
\{a\} ,每次询问给定一个区间[l,r] ,求\min_{l \le i,j \le r}\lvert a_i-a_j \rvert 。
绝对值非负,分两次处理,每次只考虑
那么从前向后枚举
若
如上图,容易知道
对于
对于
所以
询问挂在
#include<bits/stdc++.h>
#define N 300005
#define int long long
#define rd read()
#define gc pa == pb && (pb = (pa = buf) + fread(buf, 1, 100000, stdin), pa == pb) ? EOF : *pa++
using namespace std;
static char buf[100000], * pa(buf), * pb(buf);
inline int read()
{
register int x=0,s=gc;
while(!isdigit(s))s=gc;
while(isdigit(s))x=(x<<1)+(x<<3)+(s^48),s=gc;
return x;
}
const int inf=1000000001,V=inf-1;
int n,m,cnt,a[N],ans[N];
vector<pair<int,int> > q[N],v[N];
struct node{int ls,rs,mx;}t[N*20];
inline int New(){t[++cnt]={0,0,0};return cnt; }
struct seg
{
int root=0;
inline void pushup(int id){t[id].mx=max(t[t[id].ls].mx,t[t[id].rs].mx);}
inline void modify(int &id,int l,int r,int x,int v)
{
if(!id)id=New();
if(l==r)return t[id].mx=max(t[id].mx,v),void();
int mid=l+r>>1;
if(x<=mid)modify(t[id].ls,l,mid,x,v);
else modify(t[id].rs,mid+1,r,x,v);
pushup(id);
}
inline int ask(int id,int l,int r,int x,int y)
{
if(!id)return 0;
if(x>r||y<l) return 0;
if(x<=l&&y>=r)return t[id].mx;
int mid=l+r>>1;
return max(ask(t[id].ls,l,mid,x,y),ask(t[id].rs,mid+1,r,x,y));
}
inline int find(int l,int r){return ask(root,1,V,l,r);}
}T;
struct BIT
{
int c[N];
inline void clear(){memset(c,0x3f,sizeof(c));}
inline int ask(int x)
{
int s=inf;
while(x<=n)s=min(s,c[x]),x+=(x&-x);
return s;
}
inline void chk(int x,int y){while(x)c[x]=min(c[x],y),x^=(x&-x);}
}G;
inline void sol()
{
cnt=0,a[0]=inf,T.root=0;
for(int j=1;j<=n;j++)
{
int i=T.find(1,a[j]);
while(a[i]<a[j])v[j].push_back({i,a[j]-a[i]}),i=T.find((a[i]+a[j]>>1)+1,a[j]);
if(a[i]==a[j])v[j].push_back({i,0});
T.modify(T.root,1,V,a[j],j);
}
}
signed main()
{
n=rd;for(int i=1;i<=n;i++)a[i]=rd;m=rd;
for(int i=1,l,r;i<=m;i++)l=rd,q[rd].push_back({l,i});
sol();for(int i=1;i<=n;i++)a[i]=inf-a[i];
sol(),G.clear();
int cnt=0;
for(int i=1;i<=n;i++)cnt+=v[i].size();
for(int i=1;i<=n;i++)
{
for(auto [l,w]:v[i])G.chk(l,w);
for(auto [l,id]:q[i])ans[id]=G.ask(l);
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}
*例题
给定一个带边权的有
n 个点的树,m 次询问,每次询问求起点和终点在[l,r] 内的最短树上路径。
树上路径,考虑点分治。对于当前根
对于在
于是我们有若干点对
对于一个支配点对
如果
正反分别做单调栈维护,每个点贡献
扫描线,二维数点即可。时间复杂度
#include<bits/stdc++.h>
#define int long long
#define rd read()
#define gc pa == pb && (pb = (pa = buf) + fread(buf, 1, 100000, stdin), pa == pb) ? EOF : *pa++
using namespace std;
static char buf[100000], * pa(buf), * pb(buf);
inline int read()
{
register int x=0,s=gc;
while(!isdigit(s))s=gc;
while(isdigit(s))x=(x<<1)+(x<<3)+(s^48),s=gc;
return x;
}
const int N=200005,M=1000005,inf=1e16;
bool vis[N];
int n,m,rt,sz,mx[N],siz[N],ans[M];
vector<pair<int,int> > v[N],q[N],p[N],d;
inline void chkmax(int &x,int y){x=(x<y?y:x);}
inline void chkmin(int &x,int y){x=(x>y?y:x);}
inline void find(int x,int f)
{
siz[x]=1,mx[x]=0;
for(auto [i,j]:v[x])
if(i!=f&&!vis[i])find(i,x),siz[x]+=siz[i],chkmax(mx[x],siz[i]);
chkmax(mx[x],sz-siz[x]);
if(mx[x]<mx[rt])rt=x;
}
inline void get(int x,int l,int f)
{
if(vis[x])return;
d.push_back({x,l});
for(auto [i,j]:v[x])if(i!=f)get(i,l+j,x);
}
inline void sol(int x)
{
if(vis[x])return;
vis[x]=1;
for(auto [i,j]:v[x])get(i,j,x);
d.push_back({x,0}),sort(d.begin(),d.end());
stack<pair<int,int> > st;
for(auto [i,j]:d)
{
while(st.size()&&st.top().second>j)st.pop();
if(st.size())p[i].push_back({st.top().first,st.top().second+j});
st.push({i,j});
}
while(st.size())st.pop();
reverse(d.begin(),d.end());
for(auto [i,j]:d)
{
while(st.size()&&st.top().second>j)st.pop();
if(st.size())p[st.top().first].push_back({i,st.top().second+j});
st.push({i,j});
}
d.clear();
for(auto [i,j]:v[x])if(!vis[i])rt=0,sz=siz[i],find(i,x),sol(rt);
}
struct BIT
{
int c[N];
inline void chk(int x,int y){while(x)chkmin(c[x],y),x^=(x&-x);}
inline int ask(int x){int s=LONG_LONG_MAX;while(x<=n)chkmin(s,c[x]),x+=(x&-x);return s;}
}G;
signed main()
{
n=rd;for(int i=1,x,y,w;i<n;v[x].push_back({y,w}),v[y].push_back({x,w}),++i)x=rd,y=rd,w=rd;
m=rd;for(int i=1,l;i<=m;++i)l=rd,q[rd].push_back({l,i});
mx[0]=n+1,rt=0,sz=n,find(1,0),sol(rt),memset(G.c,0x3f,sizeof(G.c));
for(int i=1;i<=n;++i)
{
for(auto [l,k]:p[i])G.chk(l,k);
for(auto [l,id]:q[i])ans[id]=G.ask(l);
}
for(int i=1;i<=m;++i)cout<<(ans[i]>inf?-1:ans[i])<<'\n';
return 0;
}
*例题
给你一个长度为
n 的序列\{a\} ,共有q 次询问,每次询问给你一个区间[l,r] ,求满足l\le i,j,k\le r 且a_i,a_j,a_k 为边长可以构成三角形,a_i+a_j+a_k 的最小值。
值域倍增分块,其中第
若区间
找区间中某个块的数的个数,可以对每个块
-
若三角形中包含
\ge 2 个<2^k 的数。因为块的数量是
\mathcal{O}(\log v) 的,而[0,k) 中每个块只有\mathcal{O}(1) 个元素,故<2^k 的数在区间[l,r] 只有\mathcal{O}(\log v) 个。将这些元素全部取出。具体的,取出元素的过程分别求
l 的后继与r 的前驱,对于每个块,这可以在\mathcal{O}(n) 的时间内求出。将这些元素和B_k 的最小元素一起从小到大排列,取出元素判一判即可,记为[t_1 ,\ldots ,t_m] 。对于一种三角形选法,其最长的两条边在
\{t_m\} 必然相邻。证明考虑最大边更大不优。所以当我们在<2^k 的数中选了=2 个时,其最长边为B_k 的[l,r] 的最小值。设目前考虑的点对为
(i,j-1,j) ,则有t_j-t_{j-1}<t_i 。如果存在j'>j ,满足t_{j'}-t_{j'-1}\ge t_j-t_{j-1} ,那么j' 一定无用。因为其对应的t_{i'}\ge t_i 。于是只用考虑前缀最小的
(t_j-t_{j-1}) ,它肯定是递减的,所以直接双指针即可。这部分的时间复杂度为
\mathcal{O}(n \log v) 。 -
若三角形中包含
= 1 个<2^k 的数。判掉有数在
B_{k+1} 的情况。选了一个<2^k 的数表示我们选了2 个B_k 中的数。枚举所有
< 2^k 的数a_e ,如果我们选了它。则我们选的另外两数a_i,a_j 要满足\lvert a_i-a_j\rvert <a_e 。根据 1. 中的结论,
a_i,a_j 一定是排序后相邻的两个数。所以当\max(a_i,a_j) 最小时,(a_i+a_j) 也最小。将块中所有数从小到大排序,记为
[t_1 ,\ldots ,t_m] ,依次插入。考虑统计支配对。若当前插入了
t_p ,新增支配对一定要选p ,所以这些点对的\max(a_i,a_j)=t_p 相等,只需要让\lvert a_i-a_j\rvert 最小,这是区间最近点对。用 CF765F 的方法线段树二分,可以在\mathcal{O}(n \log n \log v) 的时间找到所有支配对。于是现在有了
\mathcal{O}(n \log v) 个支配对(I,J) ,扫描线。然后每次会找l \le I,J \le r, \lvert a_I-a_J\rvert<a_e 的最小(a_I+a_J) 。三维偏序,此时我们的复杂度不低于\mathcal{O}(n \log n \log^2 v +q \log n \log v) ,看起来不可以过。原因是维数过大。考虑什么样的询问
[l,r] ,最小值可以枚举到a_e ,若a_L,a_R 分别是e 同块前后两个数,则L < l\le r <R 。我们称[L+1,R-1] 为e 的最小值区间。因为每个块中最小值区间长度总和是
\mathcal{O}(n) 的,所以最小值区间长度一共是\mathcal{O}(n \log v) 的。如果在这些区间找\lvert a_I-a_J\rvert<a_e 的支配对,一共有\mathcal{O}(n \log ^2 v) 个。然后以(i,j,a_i+a_j+a_e) 的形式扫描线即可。扫描线的结构中修改与查询的次数分别为
\mathcal{O}(n \log^2 v) 和\mathcal{O}(q) 。使用\mathcal{O}(1)-\mathcal{O}(n^{\epsilon}) 的迭代分块即可。这部分的时间复杂度为
\mathcal{O}(n \log^2 v+q n^{\epsilon}) 。
这个做法空间复杂度是
我们只需要一边扫描线一边找支配对即可做到
时间上有点慢,但不太卡常。
#include<bits/stdc++.h>
#define rd read()
#define gc pa == pb && (pb = (pa = buf) + fread(buf, 1, 100000, stdin), pa == pb) ? EOF : *pa++
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
#define chkmin(x,y) (x=x<y?x:y)
using namespace std;
static char buf[100000], * pa(buf), * pb(buf);
inline int read()
{
register int x=0,s=gc;while(!isdigit(s))s=gc;
while(isdigit(s))x=(x<<1)+(x<<3)+(s^48),s=gc;
return x;
}
const int N=250005,M=500005,LG=24,inf=1e8,V=1e7;
const int B1=21,B2=B1*B1,B3=B1*B1*B1;
const int S1=N/B1+5,S2=N/B2+5,S3=N/B3+5;
int n,m,cnt;
int a[N],b[N],c[N],sc[N],bl[N],p[N];
int p2[N],s2[N],MX[M];
int ql[M],qr[M],ans[M],pos[N],is[M];
vector<int> G[M],P[N],K[N],K2[N];
vector<pair<int,int> > Q[N];
inline bool cmp(int x,int y){return a[x]<a[y];}
inline bool cmp2(int x,int y){return x>y;}
struct SEG
{
int mx[N<<2];
inline void modify(int id,int v){id=pos[id],mx[id]=v;for(id>>=1;id;id>>=1)mx[id]=v;}
inline int ask(int id,int l,int r,int x,int v)
{
if(mx[id]<v||l>x)return 0;
if(l==r)return l;int mid=l+r>>1,X=0;
if(mx[id<<1|1]>=v)X=ask(id<<1|1,mid+1,r,x,v);
if(!X&&mx[id<<1]>=v)X=ask(id<<1,l,mid,x,v);
return X;
}
inline int find(int l,int MX){return ask(1,1,n,l,MX);}
}fd;
struct BT
{
int L1[S1],R1[S1],L2[S2],R2[S2],L3[S3],R3[S3];
int mn1[S1],mn2[S2],mn3[S3],b1[N],b2[N],b3[N],C[N];
inline void build()
{
memset(C,0x3f,sizeof(C)),memset(mn1,0x3f,sizeof(mn1));
memset(mn2,0x3f,sizeof(mn2)),memset(mn3,0x3f,sizeof(mn3));
for(int i=1;i<=n;++i)b1[i]=(i-1)/B1+1,b2[i]=(i-1)/B2+1,b3[i]=(i-1)/B3+1;
for(int i=1;i<=b1[n];++i)L1[i]=R1[i-1]+1,R1[i]=min(n,i*B1);
for(int i=1;i<=b2[n];++i)L2[i]=R2[i-1]+1,R2[i]=min(n,i*B2);
for(int i=1;i<=b3[n];++i)L3[i]=R3[i-1]+1,R3[i]=min(n,i*B3);
}
inline int ask(int x)
{
int res=inf;
for(int i=b3[n];i>b3[x];--i)chkmin(res,mn3[i]);
for(int i=b2[R3[b3[x]]];i>b2[x];--i)chkmin(res,mn2[i]);
for(int i=b1[R2[b2[x]]];i>b1[x];--i)chkmin(res,mn1[i]);
for(int i=R1[b1[x]];i>=x;--i)chkmin(res,C[i]);
return res;
}
inline void chk(int x,int y){chkmin(C[x],y),chkmin(mn1[b1[x]],y),chkmin(mn2[b2[x]],y),chkmin(mn3[b3[x]],y);}
}sl;
struct node
{
int m1,m2,m3,mx;
inline node operator +(const node &o)const
{
node res;
res.mx=max(mx,o.mx);
if(m1<o.m1)
{
res.m1=m1;
if(m2<o.m1)res.m2=m2,res.m3=m3<o.m1?m3:o.m1;
else res.m2=o.m1,res.m3=m2<o.m2?m2:o.m2;
}
else
{
res.m1=o.m1;
if(m1<o.m2)res.m2=m1,res.m3=m2<o.m2?m2:o.m2;
else res.m2=o.m2,res.m3=m1<o.m3?m1:o.m3;
}
return res;
}
}t[N<<2],it;
struct seg
{
inline void build(int id,int l,int r)
{
if(l==r)return t[id]={b[l],inf,inf,b[l]==inf?-inf:b[l]},void();
int mid=l+r>>1;build(id<<1,l,mid),build(id<<1|1,mid+1,r);
t[id]=t[id<<1]+t[id<<1|1];
}
inline node ask(int id,int l,int r,int x,int y)
{
if(x>r||y<l)return {inf,inf,inf,-inf};
if(x<=l&&y>=r)return t[id];int mid=l+r>>1;
return ask(id<<1,l,mid,x,y)+ask(id<<1|1,mid+1,r,x,y);
}
}T;
inline void gt(int id,int l,int r)
{
if(l==r)return pos[l]=id,void();
int mid=l+r>>1;gt(id<<1,l,mid),gt(id<<1|1,mid+1,r);
}
signed main()
{
n=rd,m=rd,sc[n+1]=n+1,sc[n+2]=n+1,a[n+1]=a[0]=inf,sl.build(),gt(1,1,n);
for(int i=1;i<=n;bl[i]=__lg(a[i]),p[i]=i,s2[i]=n+1,++i)a[i]=rd;sort(p+1,p+n+1,cmp);
for(int i=1;i<=m;++i)ql[i]=rd,qr[i]=rd,ans[i]=inf,Q[qr[i]].push_back({i,ql[i]});
for(int i=0;i<=24;++i)
{
for(int j=1;j<=n;++j)
if(bl[j]==i)b[j]=a[j],c[j]=c[j-1]+1;
else b[j]=inf,c[j]=c[j-1];
T.build(1,1,n);
for(int j=n;j>=1;--j)sc[j]=(a[j]==b[j]?j:sc[j+1]);
for(int j=1;j<=n;++j)
if(a[j]==b[j])s2[j]=sc[sc[j+1]+1],p2[sc[sc[j+1]+1]]=j;
for(int j=1,cnt,x,y,l,r;j<=m;++j)if(!is[j])
{
l=ql[j],r=qr[j],cnt=c[r]-c[l-1];
if(cnt==1)G[j].push_back(a[sc[l]]);
else if(cnt==2)
{
x=sc[l],y=sc[sc[l]+1];if(a[x]>a[y])swap(x,y);
G[j].push_back(a[x]),G[j].push_back(a[y]);
}
else if(cnt>=3)
{
is[j]=i,it=T.ask(1,1,n,l,r),MX[j]=it.mx;
chkmin(ans[j],it.m1+it.m2+it.m3),G[j].push_back(it.m1);
}
}else if(i==is[j]+1)
{
l=ql[j],r=qr[j],cnt=c[r]-c[l-1];
if(cnt>0)
{
int MN=T.ask(1,1,n,l,r).m1;
for(int e:G[j])if(e>MN-MX[j]){chkmin(ans[j],e+MN+MX[j]);break;}
}
}
}
for(int i=1,j,mn,h,P;i<=m;++i)
{
mn=inf,P=G[i].size(),j=1;
while(j<P-1&&G[i][j-1]+G[i][j]<=G[i][j+1])++j;
for(;j<G[i].size()-1;++j)
{
h=G[i][j+1]-G[i][j];
if(mn>=h)
{
while(P>0&&G[i][P-1]>h)--P;
if(P<j&&G[i][P]>h)chkmin(ans[i],G[i][P]+G[i][j]+G[i][j+1]);mn=h;
}
}
}
for(int J=1,i,j;J<=n;fd.modify(j,a[j]),++J)
{
j=p[J],i=fd.find(j,1<<bl[j]);
while(a[i]<=a[j])P[j].push_back(i),i=fd.find(j,(a[i]+a[j]>>1)+1);
}
memset(fd.mx,0,sizeof(fd.mx));
for(int J=1,i,j;J<=n;fd.modify(n-j+1,a[j]),++J)
{
j=p[J],i=n-fd.find(n-j+1,1<<bl[j])+1;
while(a[i]<=a[j])P[i].push_back(j),i=n-fd.find(n-j+1,(a[i]+a[j]>>1)+1)+1;
}
for(int i=1;i<=n;++i)stable_sort(P[i].begin(),P[i].end(),cmp2);
for(int e,E=n,R;E>=1;--E){e=p[E],R=s2[e];for(int j=e+1;j<R;++j)K[j].push_back(e);}
for(int i=1,h;i<=n;++i)
{
for(int j=p2[i]+1;j<i;++j)for(int I:P[j])
{
if(I<=p2[i])break;
if(i!=I&&a[i]>abs(a[I]-a[j])&&bl[i]<bl[I])sl.chk(min(i,I),a[I]+a[i]+a[j]);
}
for(int j:P[i])
{
h=abs(a[i]-a[j]);
for(int e:K[i])
{
if(a[e]<=h)break;
if(e!=j&&bl[e]<bl[i])sl.chk(min(e,j),a[e]+a[i]+a[j]);
}
}
for(auto [id,l]:Q[i])chkmin(ans[id],sl.ask(l));
}
for(int i=1;i<=m;++i)
if(ans[i]>=inf)cout<<"yumi!\n";
else cout<<ans[i]<<'\n';
return 0;
}