洛谷P1081:开车旅行 (Treap+倍增)

题目传送门:https://www.luogu.org/problem/show?pid=1081


题目分析:这题的两个问其实是差不多的。第一问给出了X0,我们枚举起点s,就相当于变成了n个第二类询问 (s,X0)(1<=s<=n) 。于是现在原问题变成了:给出(s,x),如何快速求s开始往下走不超过x距离时,A,B各走的距离?然后用数据结构预处理出A,B到达每一个点之后会走哪个点,询问时倍增往后跳统计答案即可。调代码的时候注意A可能比B多走一步的情况,以及A,B走的距离均为0的情况。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=100100;
const int maxl=22;

const long long M1=998244353;
const long long M2=1000000007;
const long long M3=1333333331;
typedef long long LL;

struct Tnode
{
    int id,fix;
    Tnode *lson,*rson;
} tree[maxn];
Tnode *Root=NULL;
int cur=-1;

int Next1[maxn];
int Next2[maxn];

int Next[maxn][maxl];
LL disA[maxn][maxl];
LL disB[maxn][maxl];

int a[maxn];
int n,m;
LL seed,X0;
LL sumA,sumB;

int Rand()
{
    seed=(seed*M1+M2)%M3;
    return (int)seed;
}

Tnode *New_node(int v)
{
    cur++;
    tree[cur].id=v;
    tree[cur].fix=Rand();
    tree[cur].lson=tree[cur].rson=NULL;
    return tree+cur;
}

void Right_turn(Tnode *&P)
{
    Tnode *W=P->lson;
    P->lson=W->rson;
    W->rson=P;
    P=W;
}

void Left_turn(Tnode *&P)
{
    Tnode *W=P->rson;
    P->rson=W->lson;
    W->lson=P;
    P=W;
}

void Insert(Tnode *&P,int v)
{
    if (!P) P=New_node(v);
    else
        if ( a[v]<a[ P->id ] )
        {
            Insert(P->lson,v);
            if ( P->lson->fix < P->fix ) Right_turn(P);
        }
        else
        {
            Insert(P->rson,v);
            if ( P->rson->fix < P->fix ) Left_turn(P);
        }
}

int Get_prev(Tnode *P,int x,int v)
{
    if (!P) return v;
    if ( a[ P->id ]<x ) return Get_prev(P->rson,x,P->id);
    return Get_prev(P->lson,x,v);
}

int Get_succ(Tnode *P,int x,int v)
{
    if (!P) return v;
    if ( x<a[ P->id ] ) return Get_succ(P->lson,x,P->id);
    return Get_succ(P->rson,x,v);
}

int Abs(int x)
{
    if (x>=0) return x;
    return -x;
}

void Work(int s,int x)
{
    sumA=0,sumB=0;
    LL Left=x;
    for (int j=maxl-1; j>=0; j--)
        if ( Next[s][j] && disA[s][j]+disB[s][j]<=Left )
        {
            Left-=(disA[s][j]+disB[s][j]);
            sumA+=disA[s][j];
            sumB+=disB[s][j];
            s=Next[s][j];
        }
    if ( Next2[s] && Abs(a[ Next2[s] ]-a[s])<=Left) sumA+=Abs(a[ Next2[s] ]-a[s]);
}

int main()
{
    freopen("1081.in","r",stdin);
    freopen("1081.out","w",stdout);

    scanf("%d",&n);
    seed=n;
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    for (int i=n; i>=1; i--)
    {
        Insert(Root,i);
        int Prev=Get_prev(Root,a[i],0);
        int Succ=Get_succ(Root,a[i],0);

        if ( !Prev && !Succ ) continue;
        if ( Prev && Succ )
            if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
            else Prev=0;

        if (Prev)
        {
            Next1[i]=Prev;
            Prev=Get_prev(Root,a[Prev],0);
            Succ=Get_succ(Root,a[i],0);

            if ( !Prev && !Succ ) continue;
            if ( Prev && Succ )
                if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
                else Prev=0;
            if (Prev) Next2[i]=Prev;
            else Next2[i]=Succ;
        }
        else
        {
            Next1[i]=Succ;
            Prev=Get_prev(Root,a[i],0);
            Succ=Get_succ(Root,a[Succ],0);

            if ( !Prev && !Succ ) continue;
            if ( Prev && Succ )
                if ( a[i]-a[Prev]<=a[Succ]-a[i] ) Succ=0;
                else Prev=0;
            if (Prev) Next2[i]=Prev;
            else Next2[i]=Succ;
        }
    }

    //for (int i=1; i<=n; i++) printf("%d %d\n",Next1[i],Next2[i]);

    for (int i=1; i<=n; i++)
    {
        int mid=Next2[i];
        Next[i][0]=Next1[mid];
        if (Next[i][0])
        {
            disA[i][0]=Abs(a[mid]-a[i]);
            disB[i][0]=Abs(a[ Next1[mid] ]-a[mid]);
        }
    }
    for (int j=1; j<maxl; j++)
        for (int i=1; i<=n; i++)
        {
            int mid=Next[i][j-1];
            Next[i][j]=Next[mid][j-1];
            if (Next[i][j])
            {
                disA[i][j]=disA[i][j-1]+disA[mid][j-1];
                disB[i][j]=disB[i][j-1]+disB[mid][j-1];
            }
        }

    //for (int i=1; i<=n; i++) printf("%d\n",Next[i][0]);

    scanf("%lld",&X0);
    int ans=0;
    LL ansA=M1,ansB=-1;
    for (int s=1; s<=n; s++)
    {
        Work(s,X0);
        if ( sumA*ansB<ansA*sumB || ( sumA*ansB==ansA*sumB && a[s]>a[ans] ) )
        {
            if ( !sumB && ansB>0 ) ;
            else
            {
                ansA=sumA;
                ansB=sumB;
                ans=s;
            }
        }
    }
    printf("%d\n",ans);

    scanf("%d",&m);
    for (int i=1; i<=m; i++)
    {
        int s,x;
        scanf("%d%d",&s,&x);
        Work(s,x);
        printf("%lld %lld\n",sumA,sumB);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值