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;
}

Codeforces 1332G - No Monotone Triples(数据结构综合)的更多相关文章

  1. 20162327WJH实验五——数据结构综合应用

    20162327WJH实验五--数据结构综合应用 实 验 报 告 课程:程序设计与数据结构 班级: 1623 姓名: 王旌含 学号:20162327 成绩: 指导教师:娄嘉鹏 王志强 实验密级: 非密 ...

  2. 【Codeforces 722C】Destroying Array (数据结构、set)

    题意 输入一个含有 n(1≤n≤100000) 个非负整数的 a 数组和一个 1-n 的排列 p 数组,求每次删除 a[p[i]] 后,最大连续子段和(不能跨越被删除的)是多少? 分析 因为都是非负整 ...

  3. codeforces 707C C. Pythagorean Triples(数学)

    题目链接: C. Pythagorean Triples time limit per test 1 second memory limit per test 256 megabytes input ...

  4. 【codeforces 707C】Pythagorean Triples

    [题目链接]:http://codeforces.com/contest/707/problem/C [题意] 给你一个数字n; 问你这个数字是不是某个三角形的一条边; 如果是让你输出另外两条边的大小 ...

  5. 【Codeforces 707C】Pythagorean Triples(找规律)

    一边长为a的直角三角形,a^2=c^2-b^2.可以发现1.4.9.16.25依次差3.5.7.9...,所以任何一条长度为奇数的边a,a^2还是奇数,那么c=a^2/2,b=c+1.我们还可以发现, ...

  6. 洛谷 P4497 - [WC2011]拼点游戏(数据结构综合)

    题面传送门 神仙 DS. 首先关于第一问可以轻松想到一个 DP,\(dp_{i,j}\) 表示考虑到第 \(i\) 位,这一位奇偶性为 \(j\) 的最大权值,时间复杂度 \(n^2q\),可以拿到 ...

  7. 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

    http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...

  8. 20162327WJH2016-2017-2《程序设计与数据结构》课程总结

    20162327WJH2016-2017-2<程序设计与数据结构>课程总结 一.每周作业链接汇总 第一周作业:算法分析 第三周作业:查找与排序 第五周作业:有关栈的总结 第七周作业:树的有 ...

  9. (转)POJ题目分类

    初期:一.基本算法:     (1)枚举. (poj1753,poj2965)     (2)贪心(poj1328,poj2109,poj2586)     (3)递归和分治法.     (4)递推. ...

随机推荐

  1. 想要彻底搞懂大厂是如何实现Redis高可用的?看这篇文章就够了!(1.2W字,建议收藏)

    高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间. 假设系统一直能够提供服务,我们说系统的可用性是100%.如果 ...

  2. 短短 29 天,应对高峰 100W+ 访问,看浙大如何交出满分答卷

    疫情期间"停课不停教,停课不停学",线上开课第一天,浙江大学网上开课平台访问量即突破100 万次,访客数3万余人,最高峰达 1.1万人同时在线,发起课程直播2000余场,然而系统却 ...

  3. Beta版本发布计划

    Beta版本新功能 小程序v2.0新功能 新功能列表 页面 新功能描述 图片涂鸦页 增加了马赛克方块形式的涂鸦,同样支持撤销和保存 图片裁切页 增加了图片裁切功能,实现对目标图片的尺寸进行裁切 编辑图 ...

  4. alertmanager的使用

    alertmanager的使用 一.Alertanager的安装 1.下载 2.安装 3.启动 4.alertmanager和prometheus的整合 二.告警分组 1.告警规则 2.alertma ...

  5. BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶

    是不是每做道线段树进阶都要写个题解..根本不会写 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...

  6. USB OTG原理和 ID 检测原理

    OTG 检测的原理是: USB OTG标准在完全兼容USB2.0标准的基础上,增添了 电源管理(节省功耗)功能,它允许设备既可作为主机,也可作为外设操作(两用OTG).USB OTG技术可实现没有主机 ...

  7. Machine learning (8-Neural Networks: Representation)

    1.Non-linear Hypotheses 2.Neurons and the Brain 从某种意义上来说,如果我们能找出大脑的学习算法,然后在计算机上执行大脑学习算法或与之相似的算法,也许这将 ...

  8. vue中element-ui table列名lable换行问题 ---亲测

    1.lable操作 :label = "'xxxxx \n xxxxx'" // 注意 lable 的: 注:双引号内有单引号,这样才可以解析文本.需要换行的文本处添加 \n 2. ...

  9. Jmeter 踩坑记录(七)

    1.master连不上Slave机 解决方法:telnet 192.168.xx.xx 1099  看IP 端口通不通,如果通 OK,不通,检查关闭防火墙或者开放端口 2.salve 连不上 mast ...

  10. redis 内存划分

    1.数据:作为数据库,数据是最主要的部分,这部分占用的内存会被统计在used_memory中 2.进程内存:redis主进程本身运行需要占用的内存,这部分内存会被统计在used_memory_rss中 ...