【NOIP2017模拟8.8A组】Trip(trip)

Description

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

Input

第一行两个整数N,M,意义见题面。
接下来一行N个整数,第i个是a[i],意义见题面。
接下来M行,第i行两个整数Li,Ri,意义见题目。

Output

M行,第i行表示第i组游客去的景点个数。

Sample Input

6 4
3 1 7 4 5 2
1 5
2 5
2 6
4 6

Sample Output

3
3
4
3

Data Constraint

30%:N,M≤5,000
60%:N,M≤100,000
100%:N,M≤1,000,000 0≤|a[i]|≤1,000,000,000 1≤Li≤Ri≤N

题解

先考虑按照a的值构造一棵笛卡尔树,对于一个询问l,r答案就是他们的lca到他们这两条链中到l链的每一个父亲的左儿子个数以及到r那边的右儿子个数
为什么这样做是对的呢?事实上对答案有贡献的并不是那个树节点对应的序列中的位置,如果这个点在序列中的位置在[l,r]外,那么一定有另一个在[l,r]中的点可以满足题目的要求
那么我们可以tarjan求lca,单调栈构造笛卡尔树来O(n)解决这个问题

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)

using namespace std;

const int maxn=1000005;

int a[maxn],fi[2*maxn],next[2*maxn],dui[2*maxn],sc[2*maxn],st[maxn*2],hc[maxn*2];
int fa[maxn],da[maxn],son[maxn][3],zu[maxn],yo[maxn];
int go[maxn][4];
bool bz[maxn];
int i,j,k,l,n,m,x,y,ans,p,root;
bool b1;
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');
}
void add(int x,int y){
    if (!fi[x]){
        fi[x]=++l; dui[l]=y; sc[x]=l;
    } else{
        next[sc[x]]=++l; dui[l]=y; sc[x]=l;
    }
    hc[l]=i;
}
int getfather(int x){
    if (fa[x]==x) return x; else fa[x]=getfather(fa[x]);
    return fa[x];
}
void dfs(int x){
    fa[x]=x;
    bz[x]=true;
    p=fi[x];
    if (da[x]>0){
        zu[x]=zu[da[x]]; yo[x]=yo[da[x]];
        if (son[da[x]][1]==x) zu[x]++; else yo[x]++;
    }
    while (p){
        if (bz[dui[p]]==true) go[hc[p]][3]=getfather(dui[p]);
        p=next[p];
    }
    if (son[x][1]) dfs(son[x][1]);
    if (son[x][2]) dfs(son[x][2]);
    fa[x]=da[x];
}
int main(){
//  freopen("t3.in","r",stdin);
    freopen("trip.in","r",stdin);
    freopen("trip.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) a[i]=read();
    fo(i,1,m)
    {
        go[i][1]=read(); go[i][2]=read();
        add(go[i][1],go[i][2]);
        add(go[i][2],go[i][1]);
    }
    l=0;
    fo(i,1,n){
        b1=false;
        while (l>0 && a[i]>a[st[l]]) 
        {
            l--;
            b1=true;
        }
        if (b1){
            da[i]=da[st[l+1]];
            da[st[l+1]]=da[i];
            son[da[i]][2]=i;
            son[i][1]=st[l+1];
            da[st[l+1]]=i;
        } else {
            son[st[l]][2]=i;
            da[i]=st[l];
        }
        st[++l]=i;
        if (l==1) root=i;
    }
    dfs(root);
    fo(i,1,m){
        ans=-zu[go[i][3]]+zu[go[i][1]]-yo[go[i][3]]+yo[go[i][2]]+1;
        write(ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值