jzoj5246. 【NOIP2017模拟8.8A组】Trip(trip) 笛卡尔数/二维偏序

21 篇文章 0 订阅
1 篇文章 0 订阅
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

Hint

第一组游客选择路段的景点评估值序列为[3,1,7,4,5],其中3,7,5满足条件
第二组游客选择路段的景点评估值序列为[1,7,4,5],其中1,7,5满足条件
第三组游客选择路段的景点评估值序列为[1,7,4,5,2],其中3,7,5,2满足条件
第四组游客选择路段的景点评估值序列为[4,5,2],其中4,5,2满足条件
本题数据规模较大,请注意您的常数造成的影响。
在这里给出一种输入输出优化的模板,在主程序中直接调用read()即可读入一个整数,调用write(x)可以把一个int变量x输出并换行。
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');
}

xdl(worldwide_d)dalao的题,想卡log,然而并没有成功。。
一个log的做法就是预处理出每个数往左往右的最长距离,即控制范围,那么对于询问容斥一下就好,求方案就用树状数组求二维偏序。

由于我比较懒就打了笛卡尔树。
对于原序列建出笛卡尔树以后,这题就很裸了,根据笛卡尔树的性质,左边的下标比他的父亲小,右边的下标比他父亲大,那么我们只需要预处理一下左的左儿子个数和右的右儿子个数就可以了,答案就是l到r的左儿子+右儿子,用lca处理一下就好,lca倍增和tarjan都可以,只不过倍增就退化为log,tarjan就是O(nαn),还是能做到线性。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2e6+5;
const int inf=1e9;
int n,m,t[N][2],fa[N];
int l[N],r[N],left[N],right[N],suml[N],sumr[N];
int sta[N],head[N],next[N],go[N],val[N],anc,a[N],mx;
int f[N],lca[N],tot,d[N];
char c,s[10];
inline void add(int x,int y,int z)
{
    go[++tot]=y;
    val[tot]=z;
    next[tot]=head[x];
    head[x]=tot;
}
inline void get(int x)
{
    int j=0,i;
    for(d[j]=x;f[d[j]];d[j]=f[d[j-1]])j++;
    for(anc=d[j],i=0;i<j;f[d[i++]]=anc); 

}
int main()
{
    freopen("trip.in","r",stdin);
    freopen("trip.out","w",stdout);
    scanf("%d%d",&n,&m);
    a[0]=-inf;
    fo(i,1,n)
    {
        scanf("%d",&a[i]);
        if (a[i]>a[mx])mx=i;
    }
    int top=0;
    fo(i,1,n)
    {
        while (top>0&&a[sta[top]]<a[i])t[i][0]=sta[top--];
        fa[t[i][0]]=i;
        sta[++top]=i;
    }
    top=0;
    fd(i,n,1)
    {
        while (top>0&&a[sta[top]]<a[i])t[i][1]=sta[top--];
        fa[t[i][1]]=i;
        sta[++top]=i;
    }
    fo(i,0,m-1)
    {
        scanf("%d%d",&left[i],&right[i]);
        add(left[i],right[i],i);
        add(right[i],left[i],i);
    }
    top=1;
    sta[top]=mx;
    while (top>0)
    {
        int x=sta[top];
        int y;
        if (t[x][0])
        {
            y=t[x][0];
            suml[y]=suml[x]+1;
            sumr[y]=sumr[x];
            t[x][0]=0;
        }
        else if (t[x][1])
        {
            y=t[x][1];
            suml[y]=suml[x];
            sumr[y]=sumr[x]+1;
            t[x][1]=0;
        }
        else
        {
            top--;
            for(int i=head[x];i;i=next[i])
            {
                int v=go[i];
                get(v);
                lca[val[i]]=anc;
            }
            f[x]=fa[x];
            continue;
        }
        sta[++top]=y;
    }
    fo(i,0,m-1)
    printf("%d\n",suml[left[i]]+sumr[right[i]]-suml[lca[i]]-sumr[lca[i]]+1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值