[51nod 1463]找朋友

题目大意

两个序列A和B。
每次询问区间[l,r],从该区间挑出两个下标i,j,满足|Bi-Bj|处于集合S中,并要求最大化Ai+Aj的值。
S集合的大小<=10,B是n的一个排列。

扫描线

我们倒着枚举左边界l,并处理掉所有左端点为l的询问。
设f[i]表示在[l,i]中找一个j,使得符合要求的情况下最大的Ai+Aj,那么[l,r]的询问相当于在[l,r]的f中求最大值。
f用线段树维护,每次l左移,枚举S集合中的一个数,然后可以去尝试更新f(因为B是n的排列,所以一个值只对应一个位置,这就好做了)
于是就解决了。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+10;
int tree[maxn*4],a[maxn],b[maxn],c[maxn],d[15];
int q[maxn][2],h[maxn],go[maxn],next[maxn],ans[maxn];
int i,j,k,l,r,t,n,m,p,tot;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void change(int p,int l,int r,int a,int b){
    if (l==r){
        tree[p]=max(tree[p],b);
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) change(p*2,l,mid,a,b);else change(p*2+1,mid+1,r,a,b);
    tree[p]=max(tree[p*2],tree[p*2+1]);
}
int query(int p,int l,int r,int a,int b){
    if (l==a&&r==b) return tree[p];
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b);
    else return max(query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b));
}
int main(){
    scanf("%d%d%d",&n,&m,&p);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n){
        scanf("%d",&b[i]);
        c[b[i]]=i;
    }
    fo(i,1,p) scanf("%d",&d[i]);
    fo(i,1,m){
        scanf("%d%d",&q[i][0],&q[i][1]);
        add(q[i][0],i);
    }
    fd(l,n,1){
        fo(i,1,p){
            if (b[l]+d[i]<=n){
                t=c[b[l]+d[i]];
                if (t>=l) change(1,1,n,t,a[l]+a[t]);
            }
            if (b[l]-d[i]>=1){
                t=c[b[l]-d[i]];
                if (t>=l) change(1,1,n,t,a[l]+a[t]);
            }
        }
        t=h[l];
        while (t){
            ans[go[t]]=query(1,1,n,l,q[go[t]][1]);
            t=next[t];
        }
    }
    fo(i,1,m) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值