【jzoj5246】【NOIP2017模拟8.8A组】【Trip】【笛卡尔树】【tarjan-lca】

题目大意

多年之后,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想知道,每组游客会去多少个景点。

解题思路

构出笛卡尔树发现答案即l到lca是父亲左儿子点的个数加上r到lca是父亲右儿子点的个数。考虑lca左边的点,左边一定不能有比它大的数,而一个点是父亲左儿子,要么就是左边没有比他大的数,要么就是在l的左边。
点的个数可以一遍dfs算出根到当前点的答案,求lca可以离线用tarjan-lca求出。

code

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define ULL unsigned int
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=1e6+9;LL inf=1e18+7;
int n,m,lg2,gra,begin[mn],to[mn],pos[mn],next[mn],a[mn],f[mn],g[mn],
    son[mn][2],fa[mn],par[mn],ans[mn],st[mn],dep[mn],ccc;
void insert(int r,int l,int p){
    to[++gra]=l;
    pos[gra]=p;
    next[gra]=begin[r];
    begin[r]=gra;
}
int get(int x){
    if(!par[x])return x;
    return par[x]=get(par[x]);
}
void dfs(int p){
    int i=0;
    if(son[p][i]){
        f[son[p][i]]=f[p]+(i==0);
        g[son[p][i]]=g[p]+(i==1);
        fa[son[p][i]]=p;
        dfs(son[p][i]);
        par[son[p][i]]=p;
    }
    fr(i,p){
        int lca=fa[get(to[i])];
        ans[pos[i]]=f[to[i]]-f[lca]+g[p]-g[lca];
    }
    i=1;
    if(son[p][i]){
        f[son[p][i]]=f[p]+(i==0);
        g[son[p][i]]=g[p]+(i==1);
        fa[son[p][i]]=p;
        dfs(son[p][i]);
        par[son[p][i]]=p;
    }
}
int read(){
       int x=0,sig=1;
       char c;
       for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
       for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
       return x*sig;
}
void write(int x){
       if (!x) putchar('0');else{
              char s[10];
              int i,j=0;
              for (;x>0;x/=10) s[j++]=x%10;
              for (i=j-1;i>=0;i--) putchar(s[i]+48);
       }
       putchar('\n');
}
int main(){
    //freopen("trip.in","r",stdin);
    //freopen("trip.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n)a[i]=read();
    fo(i,1,n){
        int tmp=st[0];
        while(st[0]&&(a[st[st[0]]]<a[i]))st[0]--;
        if(tmp!=st[0]){
            fo(j,st[0]+1,tmp-1)son[st[j]][1]=st[j+1];
            son[i][0]=st[st[0]+1];
        }
        st[++st[0]]=i;
    }
    fo(i,1,st[0]-1)son[st[i]][1]=st[i+1];
    f[st[1]]=1;
    fo(i,1,m){
        int l=read(),r=read();
        insert(r,l,i);
    }
    dfs(st[1]);
    fo(i,1,m)write(ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值