Codeforces 题目传送门 & 洛谷题目传送门

首先打表即可发现对于任意长度 \(\ge 5\) 的序列总存在一个 Monotone triple,证明不会实在不行直接 \(5^5\) 枚举相对位置大小都可以发现,因此答案要么是 \(0\),要么是 \(3\),要么是 \(4\),我们考虑对三种情况分别分类讨论,即检查区间内是否存在长度为 \(3\) 的 Monotone triple free 的子序列,再检查区间内是否存在长度为 \(4\) 的 Monotone triple free 的子序列,这样即可确定答案。

因此我们可以大致将题目分为两个部分:

1. 检验区间中是否存在长度为 \(3\) 的 Monotone triple free 的子序列

首先显然三个位置 \(i,j,k(i<j<k)\) 能够构成 Monotone triple free 的序列当且仅当中间位置的值严格小于边上两个数的值,或者中间位置的值严格大于边上两个数的值。

考虑枚举中间位置 \(p\),我们贪心地想,我们希望这三个位置都在区间 \([l,r]\) 中,因此边上两个位置肯定离中间位置越紧凑越好,因此我们可以先预处理出四个数组:\(mxl_i\) 表示在 \(i\) 前面且大于 \(a_i\) 的离 \(i\) 最近的位置,\(mxr_i\) 表示在 \(i\) 后面且大于 \(a_i\) 的离 \(i\) 最近的位置,\(mnl_i,mnr_i\) 也可以类似地定义,单调栈可以求出。那么对于以 \(p\) 为中心的长度为 \(3\) 的序列,\((mnl_p,p,mnr_p),(mxl_p,p,mxr_p)\) 必然都是的 Monotone triple free 的序列,并且这两个序列分别是上述两种情况中最紧凑的序列——如果它们都不能包含在 \([l,r]\) 中那么所有以 \(p\) 为中心的长度为 \(3\) 的 Monotone triple free 序列肯定都不能包含在 \([l,r]\) 中了。

因此题目转化为,是否 \(\exists p\in[l,r]\) 满足 \(mnl_p\ge l,mnr_p\le r\) 或者 \(mxl_p\ge l,mxr_p\le r\)。考虑一个离线做法,我们将所有询问都挂在 \(l\) 处,然后开一个指针 \(i\) 从右到左扫描,再建立一棵线段树,每次当指针从 \(i+1\) 变到 \(i\) 时都枚举所有 \(mnl_p=i\) 的 \(p\) 并用 \(mnr_p\) 更新线段树上 \(p\) 位置上的值,\(mxl\) 也同理,回答询问时只需检验线段树上 \([l,r]\) 位置上的最小值是否 \(\le r\) 即可,输出方案只需再记录一个取到最小值的位置再稍微分下类即可。

时间复杂度 \(n\log n\)

2. 检验区间中是否存在长度为 \(4\) 的 Monotone triple free 的子序列

这个看起来有亿点点棘手,因此我们先来挖掘下性质,对于一个长度为 \(4\) 的序列而言,如果它的最大值不唯一那肯定不合法——如果这两个最大值有一个在中间两个位置就肯定存在一个三元组满足左边两个数或右边两个数相同,否则不论中间两个数大小关系如何也都会存在 monotone triple。因此这四个数只能有唯一的最大值和最小值,而如果这唯一的最大值/最小值在两边,不妨以最大值在第一个位置为例,如果后三个数存在逆序对那这个最大值与这个逆序对就存在 monotone triple,否则这三个数就构成了 monotone triple,因此四个数构成 Monotone triple free 序列的充要条件是:存在唯一的最大/小值,并且最大/小值分别在第二、三个位置。

我们考虑固定第一、四个位置 \(i,l\),那么我们肯定会选择区间 \((i,l)\) 的最大值&最小值作为第二、三个位置,即存在合法的序列当且仅当区间 \((i,l)\) 的最大值 \(>\max(a_i,a_l)\),且最小值 \(<\min(a_i,a_l)\),直接枚举肯定行不通。我们考虑预处理 \(r3_i\) 表示最小的 \(r\) 满足 \((i,r]\) 最大值 \(>a_i\) 且最小值 \(<a_i\),\(l3_i\) 表示最大的 \(l\) 满足 \([l,i)\) 最大值 \(>a_i\) 且最小值 \(<a_i\),这个可以二分+ST 表,也可以直接调用上面求出的 \(mnl_i,mnr_i,mxl_i,mxr_i\),那么当我们固定住 \(i\) 之后,合法的 \(l\) 必须 \(>r3_i\),并且 \(l3_l>i\)。

我们再求出 \(r4_i\) 表示最小的 \(l\) 满足 \(l>r3_i,l3_l>i\),这个可以通过对 \(l3\) 数组建 ST 表之后二分求出,那么 \(i,r4_i\) 恰好分别对应一个长度为 \(4\) 的 Monotone triple free 序列的 \(i,l\),并且这肯定是以 \(i\) 为第一个元素的最后一个位置下标最小的合法序列,因此对于每个询问我们需检验是否存在一个 \(i\in[l,r]\) 满足 \(r4_i\le r\),这个可以再对 \(r4\) 建 ST 表求出。最后输出方案可以维护一个取到最小值的位置求出边上两个元素,再根据这两个位置之间最大值、最小值的位置求出中间两个元素。

时间复杂度 \(n\log n\)。

那么问题就来了,为什么求长度为 \(3\) 的序列时离线了,求长度为 \(4\) 的序列时反而没有离线呢

const int MAXN=2e5;
const int LOG_N=17;
const int INF=0x3f3f3f3f;
int n,qu,a[MAXN+5],L[MAXN+5],R[MAXN+5];
vector<pii> ql[MAXN+5];
int mnl[MAXN+5],mxl[MAXN+5],mnr[MAXN+5],mxr[MAXN+5];
namespace solve3{
vector<pii> pl[MAXN+5];
tuple<int,int,int> ans[MAXN+5];
struct node{int l,r;pii val;} s[MAXN*4+5];
pii merge(pii x,pii y){
pii ret;ret.fi=min(x.fi,y.fi);
if(ret.fi==x.fi) ret.se=x.se;
else ret.se=y.se;
return ret;
}
void pushup(int k){s[k].val=merge(s[k<<1].val,s[k<<1|1].val);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r){s[k].val=mp(INF,l);return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void modify(int k,int x,int v){
if(s[k].l==s[k].r) return chkmin(s[k].val.fi,v),void();
int mid=s[k].l+s[k].r>>1;
(x<=mid)?modify(k<<1,x,v):modify(k<<1|1,x,v);
pushup(k);
}
pii query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].val;
int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return merge(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
void solve(){
build(1,1,n);
for(int i=1;i<=n;i++) pl[mxl[i]].pb(mp(i,mxr[i]));
for(int i=1;i<=n;i++) pl[mnl[i]].pb(mp(i,mnr[i]));
// for(int i=1;i<=n;i++) printf("%d %d %d %d\n",mnl[i],mxl[i],mnr[i],mxr[i]);
for(int i=n;i;i--){
for(pii p:pl[i]) modify(1,p.fi,p.se);
for(pii p:ql[i]){
int x=p.fi,id=p.se;
pii mn=query(1,i,x);
if(mn.fi<=x){
int pos=mn.se;
if(mnl[pos]>=i&&mnr[pos]<=x) ans[id]=make_tuple(mnl[pos],pos,mnr[pos]);
if(mxl[pos]>=i&&mxr[pos]<=x) ans[id]=make_tuple(mxl[pos],pos,mxr[pos]);
}
}
}
}
}
namespace solve4{
template<typename T> struct st_table{
T v[MAXN+5][LOG_N+2];int op;
void build(){
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n;j++)
v[j][i]=(op)?max(v[j][i-1],v[j+(1<<i-1)][i-1]):min(v[j][i-1],v[j+(1<<i-1)][i-1]);
}
T query(int l,int r){
int k=31-__builtin_clz(r-l+1);
return (op)?max(v[l][k],v[r-(1<<k)+1][k]):min(v[l][k],v[r-(1<<k)+1][k]);
}
};
st_table<int> st3;
st_table<pii> mn,mx,st4;
int l3[MAXN+5],r3[MAXN+5],r4[MAXN+5];
tuple<int,int,int,int> ans[MAXN+5];
void solve(){
mx.op=st3.op=1;
for(int i=1;i<=n;i++) mn.v[i][0]=mx.v[i][0]=mp(a[i],i);
mn.build();mx.build();
for(int i=1;i<=n;i++){
r3[i]=max(mxr[i],mnr[i]);
l3[i]=min(mxl[i],mnl[i]);
st3.v[i][0]=l3[i];
} st3.build();
for(int i=1;i<=n;i++){
int l=r3[i]+1,r=n,p=n+1;
while(l<=r){
int mid=l+r>>1;
if(st3.query(r3[i]+1,mid)>i) p=mid,r=mid-1;
else l=mid+1;
} r4[i]=p;st4.v[i][0]=mp(r4[i],i);
// printf("%d\n",r4[i]);
} st4.build();
for(int i=1;i<=qu;i++){
pii p=st4.query(L[i],R[i]);
// printf("%d\n",p.fi);
if(p.fi<=R[i]){
int i1=p.se,i4=p.fi;
int i2=mn.query(i1,i4).se,i3=mx.query(i1,i4).se;
if(i2>i3) i2^=i3^=i2^=i3;
// printf("%d %d %d %d\n",i1,i2,i3,i4);
ans[i]=make_tuple(i1,i2,i3,i4);
}
}
}
}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
stack<int> stk;
for(int i=1;i<=n;i++){
while(!stk.empty()&&a[stk.top()]<=a[i]) stk.pop();
mxl[i]=((stk.empty())?0:stk.top());stk.push(i);
} while(!stk.empty()) stk.pop();
for(int i=1;i<=n;i++){
while(!stk.empty()&&a[stk.top()]>=a[i]) stk.pop();
mnl[i]=((stk.empty())?0:stk.top());stk.push(i);
} while(!stk.empty()) stk.pop();
for(int i=n;i;i--){
while(!stk.empty()&&a[stk.top()]<=a[i]) stk.pop();
mxr[i]=((stk.empty())?n+1:stk.top());stk.push(i);
} while(!stk.empty()) stk.pop();
for(int i=n;i;i--){
while(!stk.empty()&&a[stk.top()]>=a[i]) stk.pop();
mnr[i]=((stk.empty())?n+1:stk.top());stk.push(i);
} while(!stk.empty()) stk.pop();
for(int i=1;i<=qu;i++) scanf("%d%d",&L[i],&R[i]),ql[L[i]].pb(mp(R[i],i));
solve3::solve();solve4::solve();
for(int i=1;i<=qu;i++){
if(!get<0>(solve3::ans[i])) printf("0\n");
else if(!get<0>(solve4::ans[i]))
printf("3\n%d %d %d\n",get<0>(solve3::ans[i]),get<1>(solve3::ans[i]),get<2>(solve3::ans[i]));
else printf("4\n%d %d %d %d\n",get<0>(solve4::ans[i]),get<1>(solve4::ans[i]),get<2>(solve4::ans[i]),get<3>(solve4::ans[i]));
}
return 0;
}

最新文章

  1. Map的五种遍历方法
  2. Linux下文件删除的原理
  3. 活学活用,webapi HTTPBasicAuthorize搭建小型云应用的实践
  4. AC日记——逃出克隆岛 (bfs)
  5. 记一次动态调用WebService
  6. WCF WEB API配置
  7. hadoop分布式部署(2014-3-8)
  8. PHP JSON 操作总结
  9. 最全的JAVA源码整合下载
  10. 怎样设制 select 不可编辑 仅仅读
  11. kNN算法学习(一)
  12. eclipse新建maven项目,修改默认jdk版本
  13. mvc下添加 EntityFramework的引用
  14. COPY ORCHARD GET 404: System.UnauthorizedAccessException: mappings.bin的访问被拒绝
  15. MongoDB CPU 利用率高,分析慢请求
  16. [svc]centos7安装优化最佳姿势
  17. Fly Vim, First-Class
  18. css3在页面中插入内容
  19. 《Linux内核与分析》第七周
  20. LeetCode 70 Climbing Stairs(爬楼梯)(动态规划)(*)

热门文章

  1. airtext初始化(一)
  2. RabbitMQ设计原理解析
  3. Spring 5 MVC 中的 Router Function 使用
  4. spring cloud config 结合 spring cloud bus实现配置自定的刷新
  5. C语言基础资料,可以看看哦
  6. 常用Java API:Math类
  7. Jquery校验中国身份证号码是否正确
  8. 转:Modelsim和Vcs+Verdi使用技巧(Linux)
  9. poj 1330 Nearest Common Ancestors (最简单的LCA)
  10. 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)