Jzoj5246 Trip

温馨提示:本文附带bgm

  多年之后,worldwideD厌倦竞争,隐居山林。
  他的家乡开始发展起了旅游业,在一条很长的主干道上,有N个旅游景点,按顺序编号为1到N。根据游客们网上的评分,第i个景点有一个评估值a[i],为了区分开不同的景点,评估值是两两不同的。
  今天有M组游客前来旅游,第i组游客选择遍历景点Li到景点Ri这一段路。他们搜到Li到Ri的所有评估值,如果对于景点j(Li≤j≤Ri),不存在景点x(Li≤x<j)满足a[x]>a[j]或不存在景点y(j<y≤Ri)满足a[y]>a[j],那么他们会进入景点j
现在worldwideD想知道,每组游客会去多少个景点。


如果不想用在线的方法,我们考虑将这个询问拆成两个部分
 1.区间从小到大升序
 2.区间从大到小降序
最后答案要-1(因为最优美那个景点肯定要算两次)
考虑第一种情况(第二种将所有询问对称再将序列翻转按照第一问做即可)
对于一个询问,我们要求的是一个l开始的上升的子序列使得这个序列字典序最小的情况下让这个子序列尽可能长(注意不是LIS!对于[1 5 2 3 4]的答案是2而不是4)
那我们可以采用倍增的思想(这题正解是dsu+笛卡尔树,并不会)
我们考虑对每一个位置i求出一个f[i]表示满足a[i]<a[f[i]]且最小的位置
没错,是不是和建树有点像?
我们对这个东西直接做和倍增一样的预处理
每次对于询问[l,r]我们可以从l开始跳,一直到f[l][j]大于了r,我们就让l跳到f[l][j-1]直到j为0为止,沿途统计答案
什么?怎么求f数组?
可以用一个map或者堆(雾)
当然最好的肯定是用一个单调栈,每次都将堆顶的元素x与当前的i比较,如果a[x]<a[i]就弹掉x让后连一条边直到a[x]>a[i],将i推入栈里继续
当然这个方法可以应用于在线,只要同时维护一正一反两个f数组就可以了
(code写的有点不好,常数大而且和上面讲解有些出入,代码实际上是从右往左跳的)

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stack>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
using namespace std;
int n,m,f[20][N],s[N],w[N][2],A[N];
stack<int> q;
inline int rd(int& x){
    char c=getchar(),g=0; x=0;
    for(;c<'0' || c>'9';c=getchar()) if(c=='-') g=1;
    for(;c>='0' && c<='9';c=getchar()) x=(x<<1)+(x<<3)+c-48;
    return g?x=-x:x;
}
inline int wr(int x){
    x>=10?wr(x/10):0; putchar(x%10+48);
}
void _18520(){
    for(int i=n;i;q.push(i--))
        for(;!q.empty()&&s[i]>s[q.top()];q.pop()) f[0][q.top()]=i;
    for(;!q.empty();q.pop()) f[0][q.top()]=0;
    for(int i=1;i<20;++i)
        for(int j=1;j<=n;++j) f[i][j]=f[i-1][f[i-1][j]];
    for(int x,y,i=1,j;i<=m;++i)
        for(j=19,x=w[i][0],y=w[i][1];;){
            for(;~j&&f[j][y]<x;--j);
            if(j<0) break;
            A[i]+=(1<<j); y=f[j][y];
        }
}
int main(){
    freopen("trip.in","r",stdin);
    freopen("trip.out","w",stdout);
    rd(n); rd(m);
    for(int i=1;i<=n;++i) rd(s[i]);
    for(int i=1;i<=m;++i) rd(w[i][0]),rd(w[i][1]);
    _18520(); 
    reverse(s+1,s+1+n);
    for(int i=1;i<=m;++i){ 
        w[i][0]=n-w[i][0]+1;
        w[i][1]=n-w[i][1]+1;
        swap(w[i][0],w[i][1]);
    }
    _18520();
    for(int i=1;i<=m;putchar(10)) wr(++A[i++]);
}
原文地址:https://www.cnblogs.com/Extended-Ash/p/7887158.html