2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17) I Intrinsic Interval
题目
给你一个排列,定义一个连续段为一个子区间,其中包含的数是连续的。
对于一个子区间,求出包含它的本源连续段,即包含它的最小的连续段。
需要处理若干个询问。
\(n,m\le 10^5\)
正解
一眼看下去这不就是析合树吗?
于是调了半天析合树,最终艰难地通过了。
当然有些比较阳间的做法:可以发现对于询问的区间\([L,R]\),找到最小的\(r\)使得存在\(l\),\([L,R]\subseteq [l,r]\)并且\([l,r]\)为连续段,此时\([l,r]\)即为答案。
反证为什么是对的:如果存在更优的区间\([l',r']\)且\(r<r'\),那么一定有\([l',r]\)为连续段(两个连续段的交为连续段)。
后面就随便做了:右端点\(r\)从左到右扫,每个询问挂在右端点上,用个线段树对于每个\(l\)维护\(max_{i=l}^r a_i-\min_{i=l}^r+l\)最小的\(l\),如果这个值等于\(r\),那么\([l,r]\)为连续段。用个\(set\)存每个询问,如果当前存在最长的连续段\([l,r]\)覆盖了某个询问,那么这个询问答案的右端点就确定了,左端点在线段树上二分就可以出来。
之前\(max_{i=l}^r a_i-\min_{i=l}^r+l\)一直都是维护两个单调栈,然而我AC之后看网上的题解发现一个似乎更方便的做法:一个连续段\([l,r]\)一定满足有\(r-l\)对\((x,x+1)\)满足\(x,x+1\)都在区间内。考虑统计有多少对。于是在加入某个数\(x\)的时候,包含\(x-1\)或\(x+1\)的区间就可以加一。维护个最大值。其它的类似。
时间复杂度都是\(O(n\lg n)\)。(不过最正宗的析合树配上tarjan LCA可以做到线性)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
int n;
int a[N];
int s0[N],t0;
int s1[N],t1;
pair<int,int> v[N*4];
int t[N*4];
pair<int,int> vmin(pair<int,int> x,pair<int,int> y){return x.first<=y.first?x:y;}
void build(int k,int l,int r){
v[k]={0,l};
if (l==r) return;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void add(int k,int l,int r,int st,int en,int c){
if (st<=l && r<=en){
v[k].first+=c,t[k]+=c;
return;
}
if (t[k]){
v[k<<1].first+=t[k],t[k<<1]+=t[k];
v[k<<1|1].first+=t[k],t[k<<1|1]+=t[k];
t[k]=0;
}
int mid=l+r>>1;
if (st<=mid) add(k<<1,l,mid,st,en,c);
if (mid<en) add(k<<1|1,mid+1,r,st,en,c);
v[k]=vmin(v[k<<1],v[k<<1|1]);
}
pair<int,int> res;
void query(int k,int l,int r,int st,int en){
if (v[k].first>=res.first) return;
if (st<=l && r<=en){
res=v[k];
return;
}
if (t[k]){
v[k<<1].first+=t[k],t[k<<1]+=t[k];
v[k<<1|1].first+=t[k],t[k<<1|1]+=t[k];
t[k]=0;
}
int mid=l+r>>1;
if (st<=mid) query(k<<1,l,mid,st,en);
if (mid<en) query(k<<1|1,mid+1,r,st,en);
}
int f[N];
struct Node{
Node *fa,*r;
int mn,mx;
bool s;//0 xi 1 he
int L,R;
} d[N*2];
int cnt;
Node *id[N];
bool judge(Node *x,Node *y){
return x->mx-x->mn+1+y->mx-y->mn+1==max(x->mx,y->mx)-min(x->mn,y->mn)+1;
}
void eat(Node *x,Node *y){
x->mn=min(x->mn,y->mn);
x->mx=max(x->mx,y->mx);
x->L=min(x->L,y->L);
x->R=max(x->R,y->R);
}
Node *q[N];
int nq;
void insert(Node *x,int l){
if (!nq || x->L<=l){
q[++nq]=x;
return;
}
Node *t=q[nq];
if (judge(t,x)){
if (t->r && judge(t->r,x)){
x->fa=t,t->r=x;
eat(t,x);
--nq;
insert(t,l);
}
else{
Node *nw=&d[++cnt];
*nw={NULL,x,min(t->mn,x->mn),max(t->mx,x->mx),1,t->L,x->R};
t->fa=x->fa=nw;
--nq;
insert(nw,l);
}
}
else{
t=&d[++cnt];
x->fa=t;
*t={NULL,x,x->mn,x->mx,0,x->L,x->R};
while (nq && q[nq]->R>=l){
q[nq]->fa=t;
eat(t,q[nq--]);
if (t->mx-t->mn+1==t->R-t->L+1){
insert(t,l);
break;
}
}
}
}
int dep[N*2],fa[N*2][18];
void getdep(Node *t){
if (dep[t-d]) return;
if (t->fa==NULL){
dep[t-d]=1;
return;
}
getdep(t->fa);
dep[t-d]=dep[t->fa-d]+1;
fa[t-d][0]=t->fa-d;
for (int i=1;1<<i<dep[t-d];++i)
fa[t-d][i]=fa[fa[t-d][i-1]][i-1];
}
int LCA(int u,int v){
if (dep[u]<dep[v]) swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1)
u=fa[u][i];
if (u==v) return u;
for (int i=17;i>=0;--i)
if (fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int kth(int u,int k){
for (int i=0;k;k>>=1,++i)
if (k&1)
u=fa[u][i];
return u;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
for (int i=1;i<=n;++i){
while (t0 && a[i]<a[s0[t0]]){
add(1,1,n,s0[t0-1]+1,s0[t0],+a[s0[t0]]);
--t0;
}
add(1,1,n,s0[t0]+1,i,-a[i]);
s0[++t0]=i;
while (t1 && a[i]>a[s1[t1]]){
add(1,1,n,s1[t1-1]+1,s1[t1],-a[s1[t1]]);
--t1;
}
add(1,1,n,s1[t1]+1,i,+a[i]);
s1[++t1]=i;
add(1,1,n,i,i,i);
res={i+1,0};
query(1,1,n,1,i);
f[i]=res.second;
}
// for (int i=1;i<=n;++i)
// printf("%d ",f[i]);
// printf("\n");
for (int i=1;i<=n;++i){
id[i]=&d[++cnt];
*id[i]={NULL,NULL,a[i],a[i],0,i,i};
insert(id[i],f[i]);
// for (int j=1;j<=nq;++j)
// printf("%d:[%d,%d] ",q[j]-d,q[j]->L,q[j]->R);
// printf("\n");
}
for (int i=1;i<=cnt;++i){
getdep(&d[i]);
// printf("%d ",d[i].fa-d);
}
// printf("\n");
int T;
scanf("%d",&T);
while (T--){
int l,r;
scanf("%d%d",&l,&r);
l=id[l]-d;
r=id[r]-d;
int lca=LCA(l,r);
if (d[lca].s==0)
printf("%d %d\n",d[lca].L,d[lca].R);
else{
l=kth(l,dep[l]-dep[lca]-1);
r=kth(r,dep[r]-dep[lca]-1);
printf("%d %d\n",d[l].L,d[r].R);
}
}
return 0;
}