题解:AT_arc218_d [ARC218D] I like Increasing
更好的阅读体验
我们称序列
先不考虑区间查询。首先我们要考虑的是,给你一个排列
我们可以先得到答案上界:我们将序列
这个为什么是上界很好理解:由于每一段递减,因此每一段内部不可能产生上升位;而相邻两个段至多产生一个上升位。因此总的上升位数量不会超过段数
接下来考虑一个基于构造的证明,可以在证明答案能取到
- 对于第一段,显然选的数越小,后面的段越容易满足条件。因此我们选第一段的最小值。
- 假设我们前一个选的数字是
x ,所属段是i 。那么- 如果
x> 第i+1 段的最大值,那么我们在第i+1 段无论选什么数都无法和第i 段产生上升位,那就取不到上界了。因此需要在第i 段多选一个数字来满足i 和i+1 段产生上升位。由于在当前段选小的数字是不劣的,我们选择当前第i 段的最小值。容易证明当前段的最小值一定小于下一段的最大值,否则这两段应当合并称一个大段。 - 如果
x< 第i+1 段的最大值,那么一定可以通过在下一段选取合适的数字满足产生上升段的条件。由于在下一段选择尽可能小的数字更容易满足以后更多段的要求,因此我们选择i+1 段中,最小的>x 的数字。
- 如果
- 当最后一个段有数字被选中的时候,答案被求得。
上述过程可以理解成,我们当前所选的数字在序列上跳的过程;而
所以我们对于每个
复杂度
#include<bits/stdc++.h>
#define endl '\n'
#define N 200006
using namespace std;
int n,q,c,a[N],bel[N],pos[20][N];
pair<int,int> b[N];
main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1,j;i<=n;i=j+1)
{
for(j=i;j<n&&a[j]>a[j+1];j++);
b[++c]={i,j};
for(int k=i;k<=j;k++)bel[k]=c;
}
bel[n+1]=c+1;
for(int j=0;j<20;j++)
for(int i=1;i<=n+1;i++)pos[j][i]=n+1;
for(int i=1;i<=n;i++)
{
if(bel[i]==c)continue;
if(a[i]>a[b[bel[i]+1].first])pos[0][i]=b[bel[i]].second;
else {
int l=b[bel[i]+1].first;
int r=b[bel[i]+1].second;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]>a[i])pos[0][i]=mid,l=mid+1;
else r=mid-1;
}
}
}
for(int i=1;i<20;i++)
for(int j=1;j<=n;j++)pos[i][j]=pos[i-1][pos[i-1][j]];
while(q--)
{
int l,r; scanf("%d%d",&l,&r);
if(bel[l]==bel[r]) {printf("1\n"); continue;}
int ans=1,st=b[bel[l]].second;
for(int i=19;~i;i--)
if(bel[pos[i][st]]<bel[r])ans+=1<<i,st=pos[i][st];
printf("%d\n",ans+1);
}
return 0;
}