题目

题目大意

给你一个排列以及若干区间,对于每个区间,问包含它的最小的优美序列的区间。

所谓优美序列,即将权值排序后能够得到连续的排列。


思考历程

优美序列显然满足这个条件:\(mx-mn=r-l\)

想了半天没有想出正解,于是开始打水法。

首先\(n,m\leq 1000\)的时候可以暴力地求出每个区间是否是优美区间,然后更新它们子区间的答案就行了。

建两棵线段树(其实如果是\(ST\)表会更好),一个按照下标建,维护区间最大最小值;

一个按照权值建,维护权值区间的最左最右下标。

离线,将所有询问丢进一个堆里,每次取出最小的区间来搞。找出最大最小值,再找出最大最小值之间的最左最右下标,将区间扩展。

如果现在这个区间之前处理过,那就直接用并查集将两者的答案合并在一起。可以在长度为第一关键字的时候以左端点为第二关键字,那么如果这个区间被处理过,它肯定是上一个区间。

扩展后的区间重新丢到堆里。如果不能扩展,就说明找到了答案。

这个方法可以得到很优秀的\(76\)分。


水法

不得不提YYT大爷的水法。

题目说是随机排列(但由于是在题面上说而不是数据上,这多少有些不可信。),按照这样的性质,可得优美序列不会太多。

于是就将所有的优美序列求出来。

枚举一个左端点\(l\),从区间\([l,l+1]\)开始,利用上面的水法进行扩展。如果扩展后左端点不为\(l\)则退出(因为已经算过了)。这样就可以处理出所有的优美序列。

后面就是一个二维偏序的问题了。


正解

题解的做法是分治。看起来好有道理,实际上……根本不知道怎么做。(题解过于简略)

题解说的时间复杂度是\(O(n\lg^2 n)\)的。

WHH在分治的基础上想到了一个\(O(n\lg^3 n)\)的做法。就在这里随便介绍一下。

同样分治。根据\(mx\)和\(mn\)分别在左边或右边分成四种情况来处理,还要用主席树来搞……

有个绝对的正解是析合树,正在学习……(WMY会了%%%)

这题就是析合树的模板啊……

晚上的时候我在床上思考,想出了一个分块的做法。

枚举右端点\(r\),左端点要满足\(l+mx_l-mn_l=r\)。设式子左边的值为\(s_l\)。

之前见过类似的题目。可以用两个单调栈分别维护\(mx_l\)和\(mn_l\),然后维护\(s_l\)。

由于我要使得等式的两边相等,所以要打分块。

对于每个分块开个桶就好了。

对于一个询问\([l,r]\),从右端点开始向后枚举,找到第一个\(i\)满足存在一个区间\([j,i]\)包含\([l,r]\)。那么这个区间就是答案(多个右端点相同的区间取最小的)。

为什么呢?如果说存在\(i<i'\),有区间\([j',i']\)也包含\([l,r]\)且长度小于\([j,i]\),那么两个区间的交\([j',i]\)必然也是个优美序列,所以答案应该是\([j',i]\)这个区间。

两个优美序列的交必定也是优美序列,证明就不再赘述了。

然后就可以\(O(n\sqrt n)\)卡过这道题。

后来发现了一个令人悲伤的真相:实际上,\(mx_l-mn_l\geq r-l\)

原因就不用说了吧……

在线段树上维护\(s_l\)的最小值就好了,寻找的时候在线段树上二分,找到满足不等式左右两边相等的\(l\)。

时间复杂度\(O(n \lg n)\),优化了好多……

我觉得这才是真正意义上的正解。分治做法还不知道是什么东西,析合树又是新的知识点,而这个方法应该是适于完全没有学过析合树的,用来锻炼思维的方法……


代码

只打了分块做法……


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 100010
#define maxK 400
inline int input(){
char ch=getchar();
while (ch<'0' || '9'<ch)
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,m;
int a[N];
int smn[N],tmn,smx[N],tmx;
struct Quest{
int l,r,num;
} q[N];
int h[N],nh;
int ansl[N],ansr[N];
inline bool cmpq(const Quest &x,const Quest &y){return x.r<y.r;}
inline bool cmph(int son,int fa){return q[son].l<q[fa].l;}
int K,nb;
int bel[N],end[N];
int tag[N];
int s[N];
int bz[maxK],hav[maxK][N],lef[maxK][N];
inline bool pushdown(int x){
if (!tag[x])
return 0;
for (int i=end[x-1]+1;i<=end[x];++i)
s[i]+=tag[x];
tag[x]=0;
return 1;
}
inline void rebuild(int x){
bz[x]++;
for (int i=end[x-1]+1;i<=end[x];++i)
if (s[i]<=n && hav[x][s[i]]!=bz[x]){
hav[x][s[i]]=bz[x];
lef[x][s[i]]=i;
}
}
inline void change(int l,int r,int c){
if (bel[l]==bel[r]){
pushdown(bel[l]);
for (int i=l;i<=r;++i)
s[i]+=c;
rebuild(bel[l]);
return;
}
pushdown(bel[l]);
for (int i=l;i<=end[bel[l]];++i)
s[i]+=c;
rebuild(bel[l]);
pushdown(bel[r]);
for (int i=end[bel[r]-1]+1;i<=r;++i)
s[i]+=c;
rebuild(bel[r]);
for (int i=bel[l]+1;i<bel[r];++i)
tag[i]+=c;
}
int main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
n=input();
for (int i=1;i<=n;++i)
a[i]=input();
K=sqrt(n);
for (int i=1;i*K<=n;++i){
++nb;
for (int j=(i-1)*K+1;j<=i*K;++j)
bel[j]=nb;
end[nb]=i*K;
}
if (n%K){
++nb;
for (int i=n/K*K+1;i<=n;++i)
bel[i]=nb;
end[nb]=n;
}
for (int i=1;i<=nb;++i)
rebuild(i);
m=input();
for (int i=1;i<=m;++i){
q[i]={input(),input(),i};
if (q[i].l==q[i].r)
ansl[i]=ansr[i]=q[i].l;
}
sort(q+1,q+m+1,cmpq);
smn[tmn=1]=smx[tmx=1]=1;
change(1,1,1);
for (int i=2,j=1;i<=n;++i){
for (;j<=m && q[j].r<=i;++j)
if (q[j].l<q[j].r){
h[nh++]=j;
push_heap(h,h+nh,cmph);
}
while (tmn && a[smn[tmn]]>a[i]){
change(smn[tmn-1]+1,smn[tmn],a[smn[tmn]]-a[i]);
tmn--;
}
smn[++tmn]=i;
while (tmx && a[smx[tmx]]<a[i]){
change(smx[tmx-1]+1,smx[tmx],-a[smx[tmx]]+a[i]);
tmx--;
}
smx[++tmx]=i;
change(i,i,i);
if (!nh)
continue;
int k,mnl=1;
for (k=1;k<=bel[i];++k)
if (hav[k][i-tag[k]]==bz[k]){
mnl=lef[k][i-tag[k]];
break;
}
k=bel[i];
while (nh && q[*h].l>=mnl){
int t=*h;
pop_heap(h,h+nh--,cmph);
for (;k>=1;--k)
if (hav[k][i-tag[k]]==bz[k] && lef[k][i-tag[k]]<=q[t].l)
break;
if (pushdown(k))
rebuild(k);
for (int ii=min(q[t].l,end[k]);ii>end[k-1];--ii)
if (s[ii]==i){
ansl[q[t].num]=ii;
ansr[q[t].num]=i;
break;
}
}
}
for (int i=1;i<=m;++i)
printf("%d %d\n",ansl[i],ansr[i]);
return 0;
}

总结

很多时候有许多隐藏的不等关系,需要细心地寻找。

然后,一定要学会析合树!

[JZOJ6279] 2019.8.5【NOIP提高组A】优美序列的更多相关文章

  1. NOIP提高组2004 合并果子题解

    NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消 ...

  2. 计蒜客 NOIP 提高组模拟竞赛第一试 补记

    计蒜客 NOIP 提高组模拟竞赛第一试 补记 A. 广场车神 题目大意: 一个\(n\times m(n,m\le2000)\)的网格,初始时位于左下角的\((1,1)\)处,终点在右上角的\((n, ...

  3. 1043 方格取数 2000 noip 提高组

    1043 方格取数  2000 noip 提高组 题目描述 Description 设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0.如下图所示(见样 ...

  4. [NOIP提高组2018]货币系统

    [TOC] 题目名称:货币系统 来源:2018年NOIP提高组 链接 博客链接 CSDN 洛谷博客 洛谷题解 题目链接 LibreOJ(2951) 洛谷(P5020) 大视野在线评测(1425) 题目 ...

  5. NOIP提高组初赛难题总结

    NOIP提高组初赛难题总结 注:笔者开始写本文章时noip初赛新题型还未公布,故会含有一些比较老的内容,敬请谅解. 约定: 若无特殊说明,本文中未知数均为整数 [表达式] 表示:在表达式成立时它的值为 ...

  6. 津津的储蓄计划 NOIp提高组2004

    这个题目当年困扰了我许久,现在来反思一下 本文为博客园ShyButHandsome的原创作品,转载请注明出处 右边有目录,方便快速浏览 题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津\ ...

  7. 2018.12.30【NOIP提高组】模拟赛C组总结

    2018.12.30[NOIP提高组]模拟赛C组总结 今天成功回归开始做比赛 感觉十分良(zhōng)好(chà). 统计数字(count.pas/c/cpp) 字符串的展开(expand.pas/c ...

  8. 2018.12.08【NOIP提高组】模拟B组总结(未完成)

    2018.12.08[NOIP提高组]模拟B组总结 diyiti 保留道路 进化序列 B diyiti Description 给定n 根直的木棍,要从中选出6 根木棍,满足:能用这6 根木棍拼出一个 ...

  9. 2013 Noip提高组 Day2

    3288积木大赛 正文 题目描述 春春幼儿园举办了一年一度的“积木大赛”.今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前 ...

  10. 2013 Noip提高组 Day1

    3285 转圈游戏 2013年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解       题目描述 Description ...

随机推荐

  1. 7_springboot2.x开发热部署

    概述:在开发中我们修改一个Java文件后想看到效果不得不重启应用,这导致大量时间花费,我们希望不重启应用的情况下,程序可以自动部署(热部署).有以下四种情况,如何能实现热部署. 1.模板引擎 在Spr ...

  2. sublime里面几个个人觉得比较实用的快捷键

    Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数名等. Ctrl+L 选中整行,继续操作则继续选择下一行,效果和 Shift+ ...

  3. .net core 根据环境变量区分正式、测试 配置文件

    新建测试.正式环境下所用的 配置信息文件 appsettings.Development.json 文件信息: { "Logging": { "LogLevel" ...

  4. java并发之同步辅助类CountDownLatch

    CountDownLatch 含义: CountDownLatch可以理解为一个计数器在初始化时设置初始值,当一个线程需要等待某些操作先完成时,需要调用await()方法.这个方法让线程进入休眠状态直 ...

  5. Nginx网站部署

    Nginx网站服务部署 常用的网站服务软件 处理静态资源的服务: apache软件:https://apache.org/ nginx软件:https://nginx.org/ 处理动态资源的服务: ...

  6. pandas--层次化索引

    层次化索引是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别. 创建一个Series,并用一个由列表或数组组成的列表作为索引. data=Series(np.random.r ...

  7. [学习笔记]最小割树(Gomory-Hu Tree)

    最小割树(\(\mathcal{Gomory-Hu Tree}\))简明指南 对于单源最短路径,我们有\(SPFA\)和\(Dijkstra\),对于多源最短路径,我们有\(Floyd\):对于两点间 ...

  8. linux 套接字

    三.命名套接字 之前的socket只是创建了一个没有名字的资源,其他进程无法访问他.所以也无法从它接受消息.只有当bind给套接字绑定了端口和名字后,其他进程才能找到它. 一般服务器是一定要bind, ...

  9. H5新增的postMessage跨域解决方案Demo

    Demo背景:html中使用iframe嵌入了跨域的vue项目,在html中将参数传入到跨越的vue项目中. 向跨越的子窗口中发送数据 function sendMessage(data) { // ...

  10. 牛客多校第八场 C CDMA 线性代数:沃尔什矩阵

    题意: 构造出一个由1和-1组成的$2^k*2^k$的矩阵,使得矩阵任意两列内积为0 题解: 数学知识,沃尔什矩阵.沃尔什矩阵的特性被CDMA(码分多址)采用,使得编码成为无线信号的频段和振幅之外的第 ...