51Nod 1463 找朋友

基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80  难度:5级算法题
 收藏
 关注
给定:
两个长度为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]互不相等
Input
n Q m
A1 A2 ....An
B1 B2 ....Bn
K1 K2 ....Km
l1 r1
l2 r2
.
.
lQ rQ
Output
Q行,每行一个整数表示相对应的答案。
如果找不到这样的两个数则输出0。
Input示例
4 2 2
1 2 3 4
3 2 1 4
1 3
1 4
2 3
Output示例
7
5
DFFXTZ  (题目提供者)

思路:(参考:cnblog 苏州城外的微笑)

好题!

先对所有查询进行离线处理,按照右区间排序,因为k一共最多只有10个,所有在该区间内的B数组,每次枚举K值,通过这样的方式来得到另外一个B值。但是这样得到的B值它在B数组中的位置必须在当前数的左边。如下图:(j为当前数在B数组中的位置,pos为计算得到的另一个B值在数组中的位置)

这两个数的和记录在pos中,这里pos的位置必须在j的左边,假设第q个查询区间如上图所示(请在脑中将pos和j互换一下),那么此时j就没包含在该区间内,这样一来,查询得到的值就会有错。因为我们是按照右区间排序的,所以右区间会不断扩大,只要左边被覆盖的,那么右边的数肯定是在该区间内的。

用线段树维护即可。具体请参见代码。




#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
         using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN=1e5+5; int n,q,m; int A[MAXN],B[MAXN],K[15],B_pos[MAXN],ans[MAXN]; struct node { int l,r,id; bool operator < (const node &rhs) const { return r < rhs.r; } } Q[MAXN]; int MAX[MAXN << 2]; int val[MAXN << 2]; void Build(int l, int r, int u) { val[u] = 0; if(l == r) return; int Mid = l + r >> 1; Build(l,Mid,u<<1); Build(Mid + 1,r,u<<1|1); } void UpDate(int l, int r, int pos, int x, int u) { val[u] = max(val[u] , x); if(l == pos && r == pos) return; int Mid = l + r >> 1; if(pos <= Mid) UpDate(l,Mid,pos,x,u<<1); else UpDate(Mid + 1,r,pos,x,u<<1|1); } int Query(int ql, int qr, int l, int r, int u) { if(ql <= l && qr >= r) return val[u]; int Mid = l + r >> 1; int res=0; if(ql <= Mid) res=max(res,Query(ql,qr,l,Mid,u<<1)); if(qr > Mid) res=max(res,Query(ql,qr,Mid + 1,r,u<<1|1)); return res; } int main() { scanf("%d%d%d",&n,&q,&m); for(int i=1; i<=n; i++) scanf("%d",&A[i]); for(int i=1; i<=n; i++) { scanf("%d",&B[i]); B_pos[B[i]]=i; } for(int i=1; i<=m; i++) scanf("%d",&K[i]); for(int i=1; i<=q; i++) { scanf("%d%d",&Q[i].l,&Q[i].r); Q[i].id=i; } sort(Q+1,Q+q+1); Build(1,n,1); int s = 1; memset(MAX,0,sizeof(MAX)); for(int i=1; i<=q; i++) { int r = Q[i].r; for(int j=s; j<=r; j++) { for(int k=1; k<=m; k++) { int tmp_B = B[j]+K[k]; int pos = B_pos[tmp_B]; //得到tmp_B在B数组中的位置 if(tmp_B <= n && pos < j && A[pos] + A[j] > MAX[pos]) { MAX[pos] = A[pos]+A[j]; //此时pos位置的最大值是由pos和[1,j)之间的一个数相加而成 UpDate(1,n,pos,MAX[pos],1); //更新线段树 } tmp_B = B[j]-K[k]; pos = B_pos[tmp_B]; if(tmp_B >= 1 && pos < j && A[pos] + A[j] > MAX[pos]) { MAX[pos] = A[pos] + A[j]; UpDate(1,n,pos,MAX[pos],1); } } } ans[Q[i].id] = Query(Q[i].l,Q[i].r,1,n,1); //查询该区间内的最大值 s = r; } for(int i=1; i<=q; i++) printf("%d\n",ans[i]); return 0; } 
       
      
      
     
     
    
    
   
   




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值