给定:
两个长度为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
题解:
网上神牛。。。
这道题很难啊,一下子居然想不出来。虽然想到是线段树,但还是很迷。
我们想将询问按左端点从大到小排序。那么对于一个点x,我们只要看一下在x之前和当前的询问的左端点之间是否存在|Bi-Bj|∈K即可。
我们逐个按询问来做,做个扫描线。对当前加入的点x,枚举A,我们看x之后是否有|By-Bx|∈K,假设存在,我们就在y的位置插入一个Ax+Ay的值。每次询问只要查询一下[l,r]之间的最大值即可。因为一个点x的假设存在值,那么和它匹配的数y一定在[l,x]的范围内。而x∈[l,r],所以y∈[l,r]。
代码(不知为何回报运行错误):
#include<bits/stdc++.h>
using namespace std;
struct aaa{
int ll1,rr1,cc;
}aa[200001];
int n,m,q,i,j,k,a[200001],b[200001],c[200001],f[200001],ans[200001],g[200001];
bool cmp(aaa a,aaa b){
return a.ll1>b.ll1;
}
int lowbit(int x){
return x&(-x);
}
void insert(int x,int t){
for(int i=x;i<=n;i+=lowbit(i)){
f[i]=max(f[i],t);
}
}
int query(int x){
int i,sum=0;
for(i=x;i;i-=lowbit(i))sum=max(sum,f[i]);
return sum;
}
int main(){
scanf("%d%d%d",&n,&q,&m);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)scanf("%d",&b[i]);
for(i=1;i<=m;i++)scanf("%d",&c[i]);
for(i=1;i<=q;i++){
scanf("%d%d",&aa[i].ll1,&aa[i].rr1);
aa[i].cc=i;
}
sort(aa+1,aa+q+1,cmp);
aa[0].ll1=n+1;
for(i=1;i<=q;i++){
for(j=aa[i-1].ll1-1;j>=aa[i].ll1;j--){
for(k=1;k<=m;k++){
if(g[b[j]+c[k]])insert(g[b[j]+c[k]],a[j]+a[g[b[j]+c[k]]]);
if(c[k]-b[j]&&g[b[j]-c[k]])insert(g[b[j]-c[k]],a[j]+a[g[b[j]-c[k]]]);
}
g[b[j]]=j;
}
ans[aa[i].cc]=query(aa[i].rr1);
}
for(i=1;i<=q;i++)printf("%d\n",ans[i]);
}