题解 [51nod 1463] 找朋友

本文详细解析了51nod1463找朋友这道题目的解题思路,采用线段树算法,通过枚举和离线询问的方法,在O(nmlogn+q)的时间复杂度下求解最大Ai+Aj值,适用于两个数列A、B及集合K的特定条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解 [51nod 1463] 找朋友

题目描述

给定:

两个长度为n的数列A 、B

一个有m个元素的集合K

询问Q次

每次询问[l,r],输出区间内满足|Bi-Bj|∈K 的最大Ai+Aj

数据约定:

n,Q<=100000

m <= 10

0<=A[i]<=1000000000

1<=B[i]<=n

1<=K[i]<=n

保证B[i]互不相等

具体做法与心路历程

这是线段树的一道好题。类似题目有HH 的项链

想的时候就觉得做法可能跟HH的项链比较像,最后差一点就想出来了,还是看了题解。

这种题还是要多想一想。

具体做法

题目告诉了我们 B B B是一个排列,且 m ≤ 10 m \leq 10 m10,我们考虑对每个数来做。

枚举 i i i,我们可以快速算出与 B i B_i Bi可以组成合法配对的另一个 B j B_j Bj,若我们对于每个 i i i都只考虑 j j j在其左边的情况,那么可以做到不重不漏。

把询问离线下来,按 r r r排序。考虑在枚举 i i i的时候统计所有 r = i r=i r=i的询问的答案。

我们把 A i + A j A_i+A_j Ai+Aj的值用线段树存在 j j j的位置( j j j i i i左边),那么询问区间时相当于询问 l , r l,r l,r的最大值。

这样扫一遍即可。时间复杂度为 O ( n m l o g n + q ) O(nmlogn+q) O(nmlogn+q)

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月29日 星期二 19时56分59秒
*******************************/
#include<cstdio>
#include<algorithm>

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

struct Query{
    int l,r,id;
    bool operator<(const Query & p) const
    {
        return r<p.r;
    }
};

const int maxn=1e5+10;
int a[maxn],b[maxn],n,m,q,s[11],ans[maxn],loc[maxn];
Query qry[maxn];

/*{{{线段树*/

namespace SegmentTree{

    int tr[maxn*4];

    void update(int k)
    {
        tr[k]=max(tr[k<<1],tr[k<<1|1]);
    }

    void modify(int k,int l,int r,int pos,int val)
    {
        if(l==r)
        {
            tr[k]=max(tr[k],val);
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
            modify(k<<1,l,mid,pos,val);
        else
            modify(k<<1|1,mid+1,r,pos,val);
        update(k);
    }

    int query(int k,int l,int r,int x,int y)
    {
        if(l>=x && r<=y) return tr[k];
        if(l>y  ||  r<x) return 0;
        int mid=(l+r)>>1;
        return max(query(k<<1,l,mid,x,y),query(k<<1|1,mid+1,r,x,y));
    }

};

/*}}}*/

void solve()
{
    int r=1;
    for(int i=1;i<=n;i++)
    {
        for(int k=1;k<=m;k++)
        {
            if(b[i]>s[k] && loc[b[i]-s[k]]<=i)
                SegmentTree::modify(1,1,n,loc[b[i]-s[k]],a[i]+a[loc[b[i]-s[k]]]);
            if(b[i]+s[k]<=n && loc[b[i]+s[k]]<=i)
                SegmentTree::modify(1,1,n,loc[b[i]+s[k]],a[i]+a[loc[b[i]+s[k]]]);
        }
        while(r<=q && qry[r].r<=i)
        {
            ans[qry[r].id]=SegmentTree::query(1,1,n,qry[r].l,qry[r].r);
            r++;
        }
    }
}

int main()
{
    //freopen("p1463.in","r",stdin);
    //freopen("p1463.out","w",stdout);
    cin>>n>>q>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i],loc[b[i]]=i;
    for(int i=1;i<=m;i++) cin>>s[i];
    for(int i=1;i<=q;i++) cin>>qry[i].l>>qry[i].r,qry[i].id=i;
    sort(qry+1,qry+q+1);
    solve();
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值