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. 初学python-day2 字符串格式化1

  2. SpringBoot整合Prometheus

    SpringBoot整合Prometheus 一.需求 二.实现步骤 1.引入jar包 2.application.prometheus文件配置 3.查看指标数据 4.接入到 prometheus 中 ...

  3. 2019.03.27【GDOI2019】模拟 T3

    题目大意 给出$n$, $p$, 求有多少长度为$n$的排列可以被分成三个上升子序列, 数量对$p$取模, 数据范围 $3 \leq n \leq 500$. 思路 首先让我们考虑如果有一个排列,如何 ...

  4. vim 常用操作技巧

    记录常用的vim操作技巧,基本满足90%的日常编辑使用. 文档操作 vim test.txt 打开当前目录下的test.txt文档,若不存在则创建该文件 :w 保存当前修改到文件 :w bak.txt ...

  5. linux exit 和 _exit的区别

    今天仔细看了一下exit和_exit这两个函数的区别,实际上exit也是调用了_exit退出函数的,只不过在调用_exit之前,exit还进行了一些多余的工作,也正是因为这样,相比起来exit就没有那 ...

  6. 链表中环的入口结点 牛客网 剑指Offer

    链表中环的入口结点 牛客网 剑指Offer 题目描述 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. # class ListNode: # def __init__(se ...

  7. 2021CCPC河南省省赛

    大一萌新,第一次打比赛,虽然是线下赛,但送气球的环节还是很赞的! 这里主要是补一下自己的弱项和考试时没有做出来的题目. 1002(链接之后再放,官方还没公开题目...) 先说一下第二题,这个题一看就是 ...

  8. hdu 5101 Select (二分+单调)

    题意: 多多有一个智商值K. 有n个班级,第i个班级有mi个人.智商分别是v1,v2,.....vm. 多多要从这些人中选出两人.要求两人智商和大于K,并且两人不同班.问总共有多少种方案. 数据范围: ...

  9. 应对gitee容量超限. 保留star/fork/评论

    应对gitee容量超限 进入企业版,"管理"-"仓库管理",点"清空仓库". 在E:\gitee目录上右击,"git bash h ...

  10. etcd安装常用操作

    etcd安装 etcd 是基于 Raft 的分布式 key-value 存储系统,由 CoreOS 开发,常用于服务发现.共享配置以及并发控制(如 leader 选举.分布式锁等).kubernete ...